cin可以用于接收输入,最常见的是从控制台接收。在刚学习C++的时候经常会用cin来接收数据,这里想要系统的总结一下cin的用法,保证不灌水。

C++中的cin是一个 istream对象,从标准输入中读取数据,在iostream头文件中定义。

流对象不能拷贝或赋值。此外,读写一个IO对象都会改变其状态,因此传递和返回的引用都不能是const的,否则无法读写。

标准缓冲区

在理解cin功能时,不得不提标准输入缓冲区。当我们从键盘输入字符串的时候需要敲一下回车键才能够将这个字符串送入到缓冲区中,那么敲入的这个回车键(\r)会被转换为一个换行符(\n),这个换行符(\n)也会被存储在cin的缓冲区中并且被当成一个字符来计算!比如我们在键盘上敲下了123456这个字符串,然后敲一下回车键(\r)将这个字符串送入了缓冲区中,那么此时缓冲区中的字节个数是7 ,而不是6。

cin读取数据也是从缓冲区中获取数据,缓冲区为空时,cin的成员函数会阻塞等待数据的到来,一旦缓冲区中有数据,就触发cin的成员函数去读取数据。cin从缓冲区读取数据,有多种方式,如操作符 >> ,函数getline()、get()等。

cin之>>

cin>>等价于cin.operator>>(),即调用成员函数operator>>()进行读取数据。

当cin>>从缓冲区中读取数据时:

  • 若缓冲区中第一个字符是空格、tab或换行这些分隔符时,cin>>会将其忽略并清除,继续读取下一个字符;
  • 若缓冲区为空,则继续等待;
  • 但是如果读取成功,字符后面的分隔符是残留在缓冲区的,cin>>不做处理。

不想略过空白字符,那就使用 noskipws 流控制。比如cin>>noskipws>>input;

// 演示操作符 >> 的用法
#include <iostream>

int main() {
    using namespace std;
    ], str2[];

    cin >> str1;
    cout << str1 << endl;
    cin >> str2;
    cout << str2 << endl;
    ;
}

测试结果:

[root@localhost cin]# ./a.out
jack   rose
jack
rose

即使"jack    rose"中间有空格,cin>>也会忽略空格(或其他空白字符)。

[root@localhost cin]# ./a.out
    jack    rose
jack
rose

cin>>对缓冲区中的第一个换行符视而不见,采取的措施是忽略清除,继续阻塞等待缓冲区有效数据的到来。

但是,getline()读取数据时,并非像cin>>那样忽略第一个换行符,getline()发现cin的缓冲区中有一个残留的换行符,不阻塞请求键盘输入,直接读取,送入目标字符串后,再将换行符替换为空字符'\0’,因此程序中的test为空串。

// 演示操作符 >> 的用法
#include <iostream>
int main() {
    using namespace std;
    ];
    while(true) {
        cin >> str1;
        cout << str1 << endl;
    }
    ;
}

现在有个问题就是:cin中输入的字符数大于数组的长度会出现什么情况?

当遇到类型不一致时,流处于不可用状态,若需继续使用这个流,需恢复流的有效状态。

get

int cin.get();

istream& cin.get(char& var);

istream& get ( char* s, streamsize n );

istream& get ( char* s, streamsize n, char delim )。

其中streamsize 在VC++中被定义为long long型。另外,还有两个重载形式不怎么使用,就不详述了,函数原型如下:

istream& get ( streambuf& sb);

istream& get ( streambuf& sb, char delim );

cin.get()从输入缓冲区读取单个字符时不忽略分隔符,直接将其读取,就出现了如上情况,将换行符读入变量b,输出时换行两次,一次是变量b,一次是endl。

cin.get()的返回值是int类型,成功:读取字符的ASCII码值,遇到文件结束符时,返回EOF,即-1,Windows下标准输入输入文件结束符为Ctrl+z,Linux为Ctrl+d。cin.get(char var)如果成功返回的是cin对象,因此可以支持链式操作,如cin.get(b)。

cin.get读取一行

读取一行可以使用istream& get ( char* s, streamsize n )或者istream& get ( char* s, size_t n, streamsize delim )。二者的区别是前者默认以换行符结束,后者可指定结束符。n表示目标空间的大小。

#include <iostream>

int main() {
    using namespace std;
    char a;
    ];
    cin.);
    cin.get(a);
    cout << "array: " << array << endl;
    cout << "a: " << (int) a << endl;
    ;
}

测试结果:

[root@localhost cin]# ./a.out
hello,world
array: hello,world
a: 

从结果可以看出,cin.get(array,20);读取一行时,遇到换行符时结束读取,但是不对换行符进行处理,换行符仍然残留在输入缓冲区。

第二次由cin.get()将换行符读入变量a,打印输入换行符的ASCII码值为10。

这也是cin.get()读取一行与使用getline读取一行的区别所在。getline读取一行字符时,默认遇到'\n'时终止,并且将'\n'直接从输入缓冲区中删除掉,不会影响下面的输入处理。

cin.get(str,size);读取一行时,只能将字符串读入C风格的字符串中,即char*,但是C++的getline函数可以将字符串读入C++风格的字符串中,即string类型。

鉴于getline较cin.get()的这两种优点,建议使用getline进行行的读取。

// 演示get获取字符
#include <iostream>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <sys/types.h>
#include <signal.h>

void catch_signal(int signal) {
    using namespace std;
    switch(signal) {
    case SIGINT:
        cout << "ctrl + c被执行了......" << endl;
        exit(-);
        break;
    }
}

int main() {
    using namespace std;
    // 注册中断信号
    signal(SIGINT, catch_signal);
    char c1;
    while(true) {
        cin.get(c1);
        cout << "c1 = " << c1 << endl;
    }
    ;
}

即get函数只会从缓冲区中取字符,而不会过滤掉任何空格换行符等。其实我这里有个疑惑,为啥cin.get(ch)可以只用变量名,而不用指针。

getline()

getline读取一行,以换行符结束,丢掉换行符。

还可指定读取多少个字符到数组,读取完后剩余的字符放在流中,流被置为无效状态,可以通过置换使他们变有效,然后继续读取,见例子。

函数作用:从标准输入设备键盘读取一串字符串,并以指定的结束符结束。

函数原型有两个:

istream& getline(char* s, streamsize count); //默认以换行符结束

istream& getline(char* s, streamsize count, char delim);

#include <iostream>

int main() {
    using namespace std;
    ];
    // 实际上只从流中读入9个字符, 最后一个字符为自动放一个0
    // getline遇到换行才结束
    cin.getline(str, );
    cout << str << endl;
    ;
}

cin >> n之后,使用getline会出现空行,调用cin.ignore()即可

如果注释掉cin.ignore(),输入2\enter,输出空行。

不注释,输入2\enterA,输出A。

因为cin有时会以\n作为结束标志,但它还在缓存区中,而getline以\n为结束标志,会读取上一次输入得到的\n,得到一个空行。

cin.ignore(1000, '\n')的含义是把缓冲区内从当前字符开始知道'\n'之前字符(如果有1000个的话)忽略掉,实际上你这里假设一行不会超过1000个字符,所以含义是忽略一行。

#include <iostream>

int main() {
    using namespace std;
    int n;
    // cin读入了之后会出现空行
    cin >> n;
    // 调用cin.ignore()可忽略空行
    cin.ignore();
    cout << "n = " << n << endl;

    ];
    cin.getline(str, );
    cout << "str = " << str << endl;
    ;
}

cin也挺有意思的,如果是数字的话,前面如果有空白或者0都会被忽略直到第一个不为0且不为空格的。

注意,cin.getline与cin.get的区别是,cin.getline不会将结束符或者换行符残留在输入缓冲区中。

cin.get()当输入的字符串超长时,不会引起cin函数的错误,后面的cin操作会继续执行,只是直接从缓冲区中取数据。但是cin.getline()当输入超长时,会引起cin函数的错误,后面的cin操作将不再执行。

cin的条件状态

IO流有四种条件状态,分别用位来控制。

使用cin读取键盘输入时,难免发生错误,一旦出错,cin将设置条件状态(condition state)。条件状态标识符号为:

cin.goodbit:       // 可用状态,无错误
cin.badbit :       // 表示系统级错误,一旦被置为该状态,流就无法再使用了
cin.eofbit :       // 表示流已经读完,到达尾部了
cin.failbit:       // 可恢复错误,如期望读取数值却读出一个字符等错误,或者已经到达流的尾部

当一个流遇到数据类型不一致的错误而不可用时,我们可以使其恢复为有效状态(置换eof和fail位):

//cin.rdstate()表示流当前的状态
cin.clear( cin.rdstate() & ~cin.failbit & ~cin.badbit );

也可通过调用成员函数查看流的状态,如:

//相应状态为真则返回true,反则为false
cin.eof()
cin.fail()
cin.bad()
cin.good()

若在输入输出类里,需要加ios::标识符号。与这些条件状态对应的就是设置、读取和判断条件状态的流对象的成员函数。

  • s.eof():若流s的eofbit置位,则返回true;
  • s.fail():若流s的failbit置位,则返回true;
  • s.bad():若流s的badbit置位,则返回true;
  • s.good():若流s的goodbit置位,则返回true;
  • s.clear(flags):清空状态标志位,并将给定的标志位flags置为1,返回void。
  • s.setstate(flags):根据给定的flags条件状态标志位,将流s中对应的条件状态位置为1,返回void。
  • s.rdstate():返回流s的当前条件状态,返回值类型为strm::iostate。strm::iostate 机器相关的整型名,由各个iostream类定义,用于定义条件状态。

了解以上关于输入流的条件状态与相关操作函数,下面看一个因输入缓冲区未读取完造成的条件状态位failbit被置位,再通过clear()复位的例子。

#include <iostream>

int main() {
    using namespace std;
    ];
    cin.getline(str, );
    // 查看good位的状态
    cout << "flag1: " << cin.good() << endl;
    // 清除错误标志
    cin.clear();
    cout << "flag1: " << cin.good() << endl;
    cin >> ch;
    cout << "str: " << str << endl;
    cout << "ch: " << ch << endl;

    ;
}

可以看出,因输入缓冲区未读取完造成输入异常,通过clear()可以清除输入流对象cin的异常状态。不影响后面的cin>>ch从输入缓冲区读取数据。

输入:[回车],输出结果为:

flag1:
flag1:
str:
ch:

因为cin.getline读取之后,输入缓冲区中残留的字符串是:5[换行],所以cin>>ch将5读取并存入ch,打印输入并输出5。

如果将clear()注释,cin>>ch;将读取失败,ch为空。

cin.clear()等同于cin.clear(ios::goodbit);因为cin.clear()的默认参数是ios::goodbit,所以不需显示传递,故而你最常看到的就是:

cin.clear()。

cin清空输入缓冲区

从上文中可以看出,上一次的输入操作很有可能是输入缓冲区中残留数据,影响下一次的输入。

那么如何解决这个问题呢?自然而然,我们想到了在进行输入时,对输入缓冲区进行清空和状态条件的复位。

条件状态的复位使用clear(),清空输入缓冲区应该使用:

函数原型:istream &ignore( streamsize num=, int delim=EOF );
函数作用:跳过输入流中n个字符,或在遇到指定的终止字符时提前结束(此时跳过包括终止字符在内的若干字符)。

程序中使用cin.ignore清空了输入缓冲区的当前行,使上次的输入残留下的数据没有影响到下一次的输入,这就是ignore()函数的主要作用。

其中,numeric_limits<std::streamsize>::max()不过是<limits>头文件定义的流使用的最大值,你也可以用一个足够大的整数代替它。

如果想清空输入缓冲区,去掉换行符,使用:

cin.ignore(numeric_limits< std::streamsize>::max());清除cin里所有内容。

cin.ignore();当输入缓冲区没有数据时,也会阻塞等待数据的到来。

有个疑问,网上很多资料说调用cin.sync()即可清空输入缓冲区。

本人测试了一下,VC++可以,但是在Linux下使用GNU C++却不行,无奈之下,linux下就选择了cin.ignore()。

其它从标准输入读取一行字符串的方法

C++中定义了一个在std名字空间的全局函数getline,因为这个getline函数的参数使用了string字符串,所以声明在了< string>头文件中了。

getline利用cin可以从标准输入设备键盘读取一行,当遇到如下三种情况会结束读操作:

  • 文件结束;
  • 遇到行分隔符;
  • 输入达到最大限度。

函数原型有两个重载形式:

istream& getline ( istream& is, string& str); //默认以换行符\n分隔行

istream& getline ( istream& is, string& str, char delim);

注意,getline遇到结束符时,会将结束符一并读入指定的string中,再将结束符替换为空字符。

因此,进行从键盘读取一行字符时,建议使用getline,较为安全。但是,最好还是要进行标准输入的安全检查,提高程序容错能力。

cin.getline()类似,但是cin.getline()属于istream流,而getline()属于string流,是不一样的两个函数。

gets读取一行

gets是C中的库函数,在< stdio.h>申明,从标准输入设备读字符串,可以无限读取,不会判断上限,以回车结束或者EOF时停止读取。

所以程序员应该确保buffer的空间足够大,以便在执行读操作时不发生溢出。

函数原型:char *gets( char *buffer );

cin.ignore()

Cin.ignore()方法cin.ignore(5, 'c')的是从输入流(cin)中提取字符,提取的字符被忽略(ignore),不被使用。

每抛弃一个字符,它都要计数和比较字符:如果计数值达到5或者被抛弃的字符是'c',则cin.ignore()函数执行终止;

否则,它继续等待。它的一个常用功能就是用来清除以回车结束的输入缓冲区的内容,消除上一次输入对下一次输入的影响。

比如可以这么用:cin.ignore(1024, '\n');通常把第一个参数设置得足够大,这样实际上总是只有第二个参数'\n'起作用,所以这一句就是把回车(包括回车)之前的所以字符从输入缓冲(流)中清除出去。

cin.sync()

清空输入缓冲区的内容。

在输入规定的数目float型数据中错误输入一个string型,然而后提示输入错误,转重新输入,直到输入正确,可以执行下面的程序

cin.clear()

cin.clear()用法如果输入发生错误发生,那么流状态既被标记为错误,你必须清除这些错误状态,以使你的程序能正确适当地继续运行。

要清除错误状态,需使用clear()函数。此函数带一个参数,它是你将要设为当前状态的标志值,只要将ios::goodbit作为实参。

cin.rdstate()

这些当前的状态信息被包含在io_state类型的对象中。io_state是一个枚举类型(就像open_mode一样),以下便是它包含的值。

goodbit 无错误

eofbit 已到达文件尾

failbit 非致命的输入/输出错误,可挽回

badbit 致命的输入/输出错误,无法挽回

有两种方法可以获得输入/输出的状态信息。一种方法是通过调用rdstate()函数,它将返回当前状态的错误标记。例如,假如没有任何错误,则rdstate()会返回goodbit.

cin.peek()  将下一字节作为int值返回但不移出它

先cin.get(ch)  把abc插入流中 当前流位置在a处

temp = cin.peek() 通过.peek() 把当前流的下一字符的副本 即为b返回给temp

所以输出b

然后通过循环 流位置在b处 再通过.peek()返回流的下一字符c给temp。

#include <exception>

int main() {
    using namespace std;

    ;
    char name[size];
    char dessert[size];

    cout << "Enter your name: ";
    // 以行的方式读取
    cin.get(name, size);
    if (cin.fail()) {
        cout << "缓冲区读取出错啦, 搞个毛线啊" << endl;
    }
    // 清空缓冲区中的所有内容
    cin.clear();
    // 清除输入缓冲区的当前行
    cin.ignore(numeric_limits<std::streamsize>::max(), '\n');
    cout << "Enter your favorite dessert: ";
    cin.getline(dessert, size);
    cout << "I have some delicious " << dessert;
    cout << " for you, " << name << endl;

    ;
}

C++中cin的用法汇总的更多相关文章

  1. js中typeof的用法汇总[转载]

    http://www.jb51.net/article/43187.htm JavaScript中的typeof其实非常复杂,它可以用来做很多事情,但同时也有很多怪异的表现.本文列举出了它的多个用法, ...

  2. Vue3中插槽(slot)用法汇总

    Vue中的插槽相信使用过Vue的小伙伴或多或少的都用过,但是你是否了解它全部用法呢?本篇文章就为大家带来Vue3中插槽的全部用法来帮助大家查漏补缺. 什么是插槽 简单来说就是子组件中的提供给父组件使用 ...

  3. C++中const指针用法汇总

    这里以int类型为例,进行说明,在C++中const是类型修饰符: int a; 定义一个普通的int类型变量a,可对此变量的值进行修改. const int a = 3;与 int const a ...

  4. 基于c语言中调试工具的用法汇总(不包含gdb)【转】

    转自:http://www.jb51.net/article/36829.htm 是不是只有编译的时候才知道程序写了错误?有没有在未编译的时候就让机器帮你检查错误的工具呢? 答案是:有!! splin ...

  5. js中typeof的用法汇总

  6. C++中cin、cin.get()、cin.getline()、getline()、gets()等函数的用法----细节决定成败 (sort用法)

    C++中cin.cin.get().cin.getline().getline().gets()等函数的用法 学C++的时候,这几个输入函数弄的有点迷糊:这里做个小结,为了自己复习,也希望对后来者能有 ...

  7. C++中cin、cin.get()、cin.getline()、getline()、gets()等函数的用法

    学C++的时候,这几个输入函数弄的有点迷糊:这里做个小结,为了自己复习,也希望对后来者能有所帮助,如果有差错的地方还请各位多多指教(本文所有程序均通过VC 6.0运行) 1.cin 2.cin.get ...

  8. Linux中find命令的用法汇总

    Linux中find命令的用法汇总 https://www.jb51.net/article/108198.htm

  9. C#中DllImport用法汇总

    最近使用DllImport,从网上google后发现,大部分内容都是相同,又从MSDN中搜集下,现将内容汇总,与大家分享. 大家在实际工作学习C#的时候,可能会问:为什么我们要为一些已经存在的功能(比 ...

随机推荐

  1. .NET Core 2.0 Cookie中间件 权限验证

    :在ConfigureServices添加Cookie中间件,使用自定义Scheme services.AddAuthentication(options=> { options.Default ...

  2. 64位平台C/C++开发注意事项

    在http://www.viva64.com/en/l/上例出了28个在64位平台上使用C/C++开发的注意事项,对于进入64位时代的程序员应该去看看这28个事项,这些英文读物对于有C/C++功底的朋 ...

  3. FreeSWITCH协议参数之自定义sip header

    一.主动发送 1. 加入sip_h_前缀 这样FreeSWITCH就能自动加上后面的扩展头. 2. 示例 <action application="set" data=&qu ...

  4. STM8串口初始化寄存器配置

    //库函数配置 UART1_DeInit(); UART1_Init((u32)1000000, UART1_WORDLENGTH_8D, UART1_STOPBITS_1, \ UART1_PARI ...

  5. 最全PyCharm教程

    最全PyCharm教程--for python PyCharm简介: PyCharm是由JetBrains打造的一款Python IDE,VS2010的重构插件Resharper就是出自JetBrai ...

  6. python2 与 python3的区别总结

    python2 与 python3的区别总结    几乎所有的Python 2程序都需要一些修改才能正常地运行在Python 3的环境下.为了简化这个转换过程,Python 3自带了一个叫做2to3的 ...

  7. spring注解方式实现定时器

    1.Spring的配置: <beans xmlns:task="http://www.springframework.org/schema/task" xsi:schemaL ...

  8. 【解决】网站运行一段时间后就无法访问,重启Tomcat才能恢复

    [背景]重新部署了网站,运行环境是Linux(centOS6.5)+Tomcat7+mysql5,作了相关安全配置和系统优化工作: [问题]运行几天后,出现了网站无法访问的现象,具体是进入网站首页时页 ...

  9. Tcp超时修改

    Linux 建立 TCP 连接的超时时间分析 tags: linux | network Linux 系统默认的建立 TCP 连接的超时时间为 127 秒,对于许多客户端来说,这个时间都太长了, 特别 ...

  10. 定时删除elasticsearch的index

    #!/bin/bashfind /data/elasticsearch/data/kz-log/nodes/0/indices/ -type d -mtime +5 |  awk -F"/& ...