问题引入
用过C++的都明白,在运用输入过程中会出现不经意间意外输入错误的情况,导致之后的输入无效,C++内部会继续使用之前的意外输入,使得程序运行错误。
输入缓冲区
所有从键盘输入的数据,不管是字符还是数字,都是先存储在内存中的一个缓冲区里面,叫做键盘缓冲区,简称输入缓冲区
或者输入流
。
当一次键盘输入结束时会将输入的数据存入输入缓冲区,而cin
函数直接从输入缓冲区中读取数据。这种缓冲机制规定,只有收到回车键时,才会将所有输入的数据一次性提交到cin
函数。回车标志一次输入的完成,如果数据不够,则会等待用户继续输入;如果数据有多余,则将多余的数据存储在输入流缓冲区中,供下次使用。
————————————————
参考链接:https://blog.csdn.net/zhao708981169/article/details/36392681
如何解决
1.cin.clear()
- C++文档:http://www.cplusplus.com/reference/ios/ios/clear/
- 作用:是用来更改
cin
的状态标示符的
这是各个状态标识符的含义:
(有道翻译)
- goodbit 无错误,
cin.rdstate()为0
- eofbit 已到达输入操作的文件尾 ,
cin.rdstate()为1
- failbit i/o操作的逻辑错误(非致命的输入/输出错误,可挽回),
cin.rdstate()为2
- badbit i/o操作的读/写错误(致命的输入/输出错误,无法挽回),
cin.rdstate()为4
比如:定义要输入到的变量是整型,但如果我们输入了其它字符,那就会发生错误。cin
里有个方法能检测这个错误,就是cin.rdstate()
; 当cin.rdstate()
返回0(即ios::goodbit)时表示无错误,可以继续输入或者操作,若返回2则发生非致命错误(即ios::failbit)则不能继续输入或操作。
#include <iostream>
using namespace std;
int main()
{
while (true)
{
int a;
cin >> a;
cout << "a=>"<<a << endl;
cout << "cin.goodbit=>" << cin.goodbit << endl;
cout << "cin.eofbit=>" << cin.eofbit << endl;
cout << "cin.failbit=>" << cin.failbit<< endl;
cout << "cin.badbit=>" << cin.badbit << endl;
cout << "cin.rdstate()=>" << cin.rdstate() << endl;
system("pause");
}
return 0;
}
// 依次输入:1,2,#(之后无法继续输入)
加几行代码,调用cin.clear()
,输入“#”依旧无法继续输入,但是可以把状态为都清掉(cin.rdstate()
返回goodbit,即0),看结果:
#include <iostream>
using namespace std;
int main()
{
while (true)
{
int a;
cin >> a;
cout << "a=>"<<a << endl;
cout << "cin.goodbit=>" << cin.goodbit << endl;
cout << "cin.eofbit=>" << cin.eofbit << endl;
cout << "cin.failbit=>" << cin.failbit<< endl;
cout << "cin.badbit=>" << cin.badbit << endl;
cout << "cin.rdstate()=>" << cin.rdstate() << endl;
cout << "调用cin.clear()后---" << endl;
cin.clear();
cout << "cin.rdstate()=>" << cin.rdstate() << endl;
system("pause");
}
return 0;
}
// 输入:#(之后依旧无法继续输入)
结论:
cin.clear()可以让状态位都变为正常,但无法清除输入缓冲区
2.cin.sync()
- C++官方文档:http://www.cplusplus.com/reference/istream/istream/sync/?kw=cin.sync
- C++中文参考文档:https://zh.cppreference.com/w/cpp/io/basic_istream/sync
- 作用:清除输入缓冲区全部的内容。成功时返回0,失败时badbit会置位,函数返回-1。
但是!!!
并不是所有时候都有效,具体可以参考这篇博文:
———————————————— https://www.cnblogs.com/seamusopen/p/8451883.html
大致意思说: 对于 std::cin 这些标准库「自带」的输入流来说,调用 sync() 是「实现定义」的行为,如果没有得到预期的效果的话,可以查看自己的编译器(标准库实现)的说明文档,上面应该有写明它所使用的是哪种行为。
以下是一些无用的查证,有兴趣可以看看我的查证过程…
好奇心下,我打开我的VS2019命令行提示工具(工具-命令行-开发者命令提示工具,输入RC/?
)
但是查找了官网和GitHub并没有找到关于编译器的说明文档…但至少知道了微软的C++的IDE用的是MSVC编译器。
直接看sync函数的定义不就行了?<istream>
的原文档,里面对sync函数这样定义:
int __CLR_OR_THIS_CALL sync() { // synchronize with input source
const sentry _Ok(*this, true);
const auto _Rdbuf = _Myios::rdbuf();
if (!_Rdbuf) {
return -1;
}
bool _Sync_failed = true; // sync fails if an exception is thrown
_TRY_IO_BEGIN
_Sync_failed = _Rdbuf->pubsync() == -1;
_CATCH_IO_END
if (_Sync_failed) {
_Myios::setstate(ios_base::badbit);
return -1;
}
return 0;
}
(结合中文文档给出解释)
将输入缓冲区与关联数据源同步。表现为无格式输入函数 (UnformattedInputFunction) ,除了不影响 gcount() 构造并检查 sentry 对象后,若 rdbuf() 为空指针,则返回 -1 ;否则调用 rdbuf()->pubsync() 。若该函数返回 -1 ,则调用 setstate(badbit) 并返回 -1 ,否则返回 0 。
但注意到中文文档这句话:
为了令下个读取操作拾取任何在流缓冲最后填充其获取区后,可能已对关联输入序列做出的更改。为达成之, sync() 可以清空获取区,或重填充它,或不做任何事。值得注意的例外是 Visual Studio ,其中此操作在以标准输入流调用时舍弃未处理的输出。
为什么我用的MSVC就是个例外了,就不香了!
用代码看看:
#include <iostream>
using namespace std;
int main()
{
while (true)
{
int a;
cin >> a;
cout << "a=>"<<a << endl;
cout << "cin.goodbit=>" << cin.goodbit << endl;
cout << "cin.eofbit=>" << cin.eofbit << endl;
cout << "cin.failbit=>" << cin.failbit<< endl;
cout << "cin.badbit=>" << cin.badbit << endl;
cout << "cin.rdstate()=>" << cin.rdstate() << endl<<endl;
cout << "调用cin.clear()后---" << endl;
cin.clear();
cout << "cin.rdstate()=>" << cin.rdstate() << endl<<endl;
cout << "调用cin.sync()后---" << endl;
cin.sync();
cout << "cin.rdstate()=>" << cin.rdstate() << endl << endl;
system("pause");
}
return 0;
}
可以看到,输入“#”依旧无法继续输入,a的值依旧是0。
再改改代码,让另一个字符串来接收缓冲区的“#”,看看是不是真的没有清除:
#include <iostream>
#include<string>
using namespace std;
int main()
{
while (true)
{
int a;
string str;
cin >> a;
cout << "a=>"<<a << endl;
cout << "cin.goodbit=>" << cin.goodbit << endl;
cout << "cin.eofbit=>" << cin.eofbit << endl;
cout << "cin.failbit=>" << cin.failbit<< endl;
cout << "cin.badbit=>" << cin.badbit << endl;
cout << "cin.rdstate()=>" << cin.rdstate() << endl<<endl;
cout << "调用cin.clear()后---" << endl;
cin.clear();
cout << "cin.rdstate()=>" << cin.rdstate() << endl<<endl;
cout << "调用cin.sync()后---" << endl;
cin.sync();
cout << "cin.rdstate()=>" << cin.rdstate() << endl << endl;
cin >> str;
cout << "str=>" << str << endl;
system("pause");
}
return 0;
}
//输入:#,1,mystr
可以看到,接下来的str我并没有输入,但接收到了缓冲区的字符“#”,而下一次循环依旧可以继续输入了。
结论
在不同编译器下,sync函数的实现不同,使得其功能有所不同,我用的是MSVC,不能执行清除缓冲区的功能。
3.cin.ignore()
- C++文档:http://www.cplusplus.com/reference/istream/istream/ignore/?kw=cin.ignore
- 作用:从输入流(cin)中提取字符,提取的字符被忽略(ignore),不被使用。
具体用法:
cin.ignore(int intExp, char chExp);
其中intExp 是一个整型表达式,也可以是一个整型数值,这个数值表示在一行中忽略的字符的最大数目,比如说intExp=100;还有一个参数chExp,是一个字符表达式。表示如果遇到一个字符值等于chEXP,那么就停止ignore(),如果ignore100个字符之后还没遇到值等于chEXP的字符,那也得停止ignore(),所以100是ignore()所忽略的最大字符数。
————————————————
原文链接:https://blog.csdn.net/imkelt/article/details/52202002
cin.ignore(numeric_limits<std::streamsize>::max(),’\n’);//清除输入缓冲区的当前行
cin.ignore(numeric_limits<std::streamsize>::max()); //清除输入缓冲区里所有内容
cin.ignore()//清除一个字符
numeric_limits<std::streamsize>::max()
不过是climits
头文件定义的流使用的最大值,你也可以用一个足够大的整数代替它。
————————————————
原文链接:https://blog.csdn.net/selina8921/article/details/79067941
看代码:
#include <iostream>
#include<string>
using namespace std;
int main()
{
while (true)
{
int a;
string str;
cin >> a;
cout << "a=>"<<a << endl;
cout << "cin.goodbit=>" << cin.goodbit << endl;
cout << "cin.eofbit=>" << cin.eofbit << endl;
cout << "cin.failbit=>" << cin.failbit<< endl;
cout << "cin.badbit=>" << cin.badbit << endl;
cout << "cin.rdstate()=>" << cin.rdstate() << endl<<endl;
cout << "调用cin.ignore()--" << endl;
cin.ignore();
cout << "输入str:";
cin >> str;
cout << "str=>" << str << endl;
system("pause");
}
return 0;
}
//输入:1,#
可以看出:在调用了cin.ignore()函数之后,str无法取出错误的字符“#”,表示忽略了缓冲区内容,但还是不能继续输入str,原因在于没有调用cin.clear()使状态位还原。
第二次测试,依次输入:###
可以看出:在没有加cin.clear()的情况下,str无论如何都无法获取缓冲区内容,即使cin.ignore()只忽略1个字符,而且也无法继续输入。
在cin.ignore()之前加上cin.clear()函数的调用,进行第三次测试,依次输入:###,1,my
可以看出:在调用了cin.ignore()函数之后,str可以取出错误的字符“##”,表示忽略了缓冲区内容,但仅仅忽略了一个字符,下一次,又可以继续输入a和str。
结论:
要使得缓冲区完全清空,最稳妥的方案:
cin.clear();
cin.ignore(numeric_limits<std::streamsize>::max())