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;
}