从一个简单的问题谈起
昨天做题的时候,遇到一个输入是这样的
3 4
aaaa aaaa aaaa
bbbbbbb bbbb
ccc cccccccc
^D
第一行输入一个整数n,表示接下来句子的数量,和另外一个整数(这里没有作用,只是用来说明下面的一个情况),接下来就是n个句子,每个句子以回车结束。
我的想法是
int i, j;
cin >> i >> j;
while(i--){
string s;
getline(cin, s);
}
可是,结果是我第一次调用getline返回的是“”(空字符串)。这是为什么呢?
原因是cin有它的缓冲区,当我们在标准输入输入回车之后,会将我们的输入放到它的缓冲区,然后cin就开始在缓冲区读。 cin的>>操作符,默认是以空格,制表符作为分隔符的。
当我们输入 3 4
换行之后,输入缓冲区读到的内容是3 4\n
,cin >> 操作符将3 4
读取之后赋给i,j,缓冲区里面剩下\n
,下一个getline来读的时候就返回空字符串了。
一个解决方法,用cin.get()吸收掉缓冲区的\n
字符。
int i, j;
cin >> i >> j;
cin.get();
while(i--){
string s;
getline(cin, s);
}
其实,我之前一直认为cin >> 操作符是以空格,制表符,回车作为分隔符的,对于代码cin >> i >>j;
,我曾经这么输入。
2
3
这样i,j读到的也是2,3,所以我就以为cin >> 也可以以回车作为分隔符。今天我查资料才发现,cin >> j的时候,缓冲区里面是有\n3
的,只是它为j赋值的时候忽略掉\n
而已。
cin的状态
在找这个问题的原因的时候我发现一个更有意思的事情。
cin.clear()
int a;
char b;
int ret = 0;
cout << "input a:" << endl;
cin >> a;
cout << "a:" << a << endl;
cout << "input b:" << endl;
cin >> b;
cout << "b:" << b << endl;
这行代码如果输入的不是数字而是字符的话,会导致一个死循环,因为第一次赋值给a的时候,赋值失败了,cin就会给自己设一个状态iostate state = badbit
这就会导致cin不能正常工作,cin >> b也不能正常工作了。
下面是我的输入:
➜ stl git:(master) ✗ g++ input.cpp -o input
➜ stl git:(master) ✗ ./input
input a:
asdf
a:0
input b:
b:
这里我给a赋了个字符,然后cin >> b就直接返回了,没有再给机会输入b,也没有办法让b正确赋值。
int a;
char b;
int ret = 0;
cout << "input a:" << endl;
cin >> a;
cout << "a:" << a << endl;
cin.clear(); //加了这一句
cout << "input b:" << endl;
cin >> b;
cout << "b:" << b << endl;
在cin错误之后,将状态设置回iostate state = goodbit
,这样我的b就能从缓冲区读取我之前的输入。我的输入
➜ stl git:(master) ✗ g++ input.cpp -o input
➜ stl git:(master) ✗ ./input
input a:
asdf
a:0
input b:
b:a
cin.ignore() and cin.sync()
网上有人说这两个语句都是用来清空输入缓冲区,也就是说回到我第一个谈到的问题,我输入数字之后回车,\n
存放在输入缓冲区的最开始,那我就可以通过这两个语句来将输入缓冲区清空,然后再读取其他输入。
cin.ignore()是丢弃输入缓冲区的一个字符,istream& ignore (streamsize n = 1, int delim = EOF);
,还可以丢弃n个字符,或抛弃到以delim结尾字符。 这是推荐使用的清空缓冲区的方法。
cin.sync()这个的实际功能依赖于具体实现,我在g++中使用这个函数没有作用,后来在下面这篇文章看到原因。
这篇文章讲到了cin.sync()具体功能依赖于实现, VC++将cin.sync()实现为清空现有缓冲区,而g++什么都不做。