cpp的cin>>和getline,getchar是三种最常见的获得控制台输入的方法。这些方法处理输入流的方法不同,有很多需要注意的地方。如果不加注意则很容易在输入上犯错误。

cin

cin>>会忽视空白字符包括空格、制表符和回车然后去读取后面可以读取的内容。这种忽略会顺便把空白字符从输入流中清除掉。但是读取的内容后面的空白字符不会被清除掉。例如cin>>int如果遇到” 123 \n”这样的输入流,那么读取到int类型整数里的数值是123,此时输入流里还会残留” \n”。

不仅对整数和浮点数是这样的,对任何字符,字符串类型和字符数组都是如此。这意味着读到的字符不会存在空白字符。cin>>char也不能把空白字符读入char中。

另外还有不得不讨论到的一个问题就是cin的状态标记。cin有四个状态标记,分别是

  • goodbit:无错误
  • eofbit:已到达文件尾
  • failbit:非致命的输入/输出错误,可挽回
  • badbit:致命的输入/输出错误,无法挽回

其中重点关注goodbit就可以应付很多问题了。通过cout << cin.good()可以查看到cin的goodbit的状态标记。这个状态标记在正常读取的情况下都是1,但是如果遇到异常就会变成0。异常中主要包含两种:遇到输入结束标志和遇到非法输入。

输入是否合法是和cin的下一个容器相关的,例如cin>>int,cin下一个要把输入处理成一个整数交给整型变量,但若此时输入流的下一个非空白字符根本就不是数字,那么在这个案例中输入就是非法的,cin不会给变量赋值而且会将自己的标记位置为0。

输入结束标志非常特殊。由于博主用的windows系统,这里只讨论windows的visual studio2017调试环境下的输入结束。ctrl+D在这里没有任何用。ctrl+Z除非是作为行的开头,才能代表输入结束,也就是说如果ctrl+Z前面有任何除换行以外的字符(包括空格和制表符),ctrl+Z都会被当成一个普通的字符(ascii=26)处理,甚至可以被cin>>char或者getchar()读入。作为结束标志的ctrl+Z被读入时,不管后面还有没有内容,都会判定为输入结束,而且缓冲区会被清除。例如输入一行”^Zabdc\n”,那么输入会结束而且输入缓冲区重置为空。

需要注意的是,既然说是状态标志,只有遇到相应的条件才会发生状态转移。而cin.clear()函数就是将所有标志位重置的方法,也就是回归到1000状态(这里默认四位状态从左到右分别是goodbit, eofbit, failbit, badbit)。在碰到上面两种异常的时候,状态都会发生不同的变化:

  • 如果是非法输入,状态变成0010
  • 如果是输入结束,状态变成0110,即fail和eof都为1

这里可以看出fail位置为1的条件是输入失败,即没有成功向输入的对象赋值。所以输入结束的时候fail也为1是可以理解的,因为输出的对象一定没有在ctrl+Z之前获得任何赋值,所以判定为输入失败。

那么讨论了上面这么多,到底有什么实际的作用呢?请注意我们经常会使用到的while(cin>>)格式。我之前总是很疑惑,这里while跳出的条件究竟是什么?因为按理说cin>>应该会返回一个输入流对象,怎么能把这样一个对象放入条件中?

现在可以理解了。作为条件的cin>>的值其实就是cin.good()。也就是说,在碰到任何非法输入和输入结束的时候都会跳出。如果输入的对象是整型,碰到任何非空白非数字的字符或者结束标志ctrl+Z都会跳出。而如果输入对象是字符或者字符串、字符数组,结束标志才能使它跳出。

getline

这个没什么可说的,参数中可以指定结束的标志。如果不指定就是默认的换行符。

唯一一点就是,遇到结束标志的时候,getline()会把结束标志从输出流中取出,但是也不把结束标志放在结果的字符串里。

getchar()

来自C语言的很经典的输入函数了。单纯从缓冲区里读入一个字符,空白字符亦可。

需要注意的是,getchar()的输入也会被结束标志ctrl+Z结束掉(和上面的讨论一致,必须是在一行开头的ctrl+Z才可作为结束标志,前面有任何其他字符都为导致ctrl+Z被认为是普通的字符)。被结束标志中断后函数的返回值是-1。

暂时先讨论这些,如果以后碰到更多再做补充。

附上测试代码:

#include<iostream>
using namespace std;

int main() {
    int num;
    char c;
    char str[10];
    cout << cin.good() << cin.eof() << cin.fail() << cin.bad() << endl;
    while (cin >> num) {
        cout << "1:" << num << endl;
        cout << cin.good() << endl;
    }
    
    c = getchar();
    cout << int(c) << endl;
    char c1 = getchar();
    cout << int(c1) << endl;

    cout << cin.good() <<cin.eof()<<cin.fail()<<cin.bad()<< endl;
    cin.clear();
    cout << cin.good() << cin.eof() << cin.fail() << cin.bad() << endl;

    while (cin>>num)
    {
        cout << "2:" << num<<endl;
    }
    cout << cin.good() << cin.eof() << cin.fail() << cin.bad() << endl;
}