需求:
- 输入字符
- 可选择整行输入和逐字输入两种模式
问题代码:
#include<iostream>
#include<string>
using namespace std;
int main()
{
int choice;
cout << "整行输入请输入1,逐字输入请输入0\n";
cin >> choice;
if (choice==1)
{
string content_str;
cout << "现在是整行输入模式\n"<<"请整行输入你要输入的内容\n";
while (getline(cin, content_str))
{
cout << content_str << endl;
//if (content_str.empty())
//{
// break;
//}
}
}
else if(choice==0)
{
cout << "现在是逐字输入模式\n" << "请逐字输入你要输入的内容\n";
char content_str;
while (cin>>content_str )
{
cout << content_str<<endl;
}
}
else
{
cout << "输入有误\n";
}
return 0;
}
我们choice输入1进入整行输入模式,运行结果如下:
很奇怪,还没有cin,就已经进入了结构体,运行了cout语句了。看来先进入循环后,执行一次再进行条件判断吧?
为了检测,这里把string content_str;
- 修改为string content_str(“初始值”);
再运行:
这一结果推翻了之前的推测,现在情况有些难以判断,所以设置断点进行debug:
进入while之前,初始值正确设置,讲道理,如果不先经过条件判断,直接运行语句的话,直接输出的应该是“初始值”而非空行。
这里继续下一句,奇怪的事情发生了:
可以看到,本次并没有提示控制台输入,而是直接进入了结构体,一进去content_str的值就被清空了! - 个人认为这个错误是很严重的,如果我想以空行为条件判断,那么在程序一运行时,就直接进入了空行条件结构体了。
很奇怪啊,结构体是在content_str的作用域内的,为什么一进去就清空了呢?我怀疑是getline的作用,使cin直接接受了回车,将回车推到了content_str内,所以输出了空行。
那么回车是从哪里来的呢?分析整个流程:
- 在确定输入模式的时候,我在控制台敲了1+回车
- 在conten_str的定义下,输出的“ cout << “现在是整行输入模式\n”<<“请整行输入你要输入的内容\n”; ”当中有两个换行符
先考虑第一个情况,这里把模式1的代码块拿出来单独运行:
果然!空行消失了
问题找到了,那,到底是什么原因,使前面cin输入的1+回车影响到了content_str呢?
原来键盘是输入到缓冲区的,cin 和cin.getline()实际上是从缓冲区读入数据,当缓冲区有内容时,会直接从缓冲区读取,不会要求键盘输入。
- cin只是在缓存区中,把字符读走,会剩余/n在缓存区中
- 但是getline对/n极度敏感,导致getline刚开始读入便遇到/n于是停止读入数据。
因此,要在cin与getline之间加入
- cin.get();
- 或cin.ignore();
这样,程序就可以正常运行了,吸收了换行符后,可以加入循环退出语句:
if (content_str.empty())
{
break;
}
其中cin.get()用来舍弃输入流中不需要的字符 或者舍弃回车。
cin.ignore( a, ch )表示从 cin 中提取并忽略字符。而每抛弃一个字符,它都要进行计数和比较字符:如果计数值达到 a 或者被抛弃的字符是 ch ,则cin.ignore() 函数执行终止;否则,它继续等待。
- cin.ignore(1024, ‘\n’),通常把第一个参数设置得足够大,这样实际上是为了只有第二个参数 ‘\n’
起作用,所以这一句就是把回车(包括回车)之前的所以字符从输入缓冲流中清除出去。 - 如果默认不给参数的话,默认参数为cin.ignore(1, EOF),即把EOF前的1个字符清掉,没有遇到EOF就清掉一个字符然后结束
最后再谈一谈string的输入运算符和getline函数是如何处理空白符的:
- string的输入运算符自动忽略字符串开头的空白(包括空格符、换行符、制表符),从第一个真正的字符开始,直到遇见下一处空白为止。
- 如果需要保留输入时的空白符,应用getline函数替代原来的>>运算符,getline从给定的输入流中读入数据,直到换行符为止,此时换行符也被读取进来,但是并不储存在最后的字符串里