在C ++中清除输入缓冲区

问题引入

用过C++的都明白,在运用输入过程中会出现不经意间意外输入错误的情况,导致之后的输入无效,C++内部会继续使用之前的意外输入,使得程序运行错误。

输入缓冲区

所有从键盘输入的数据,不管是字符还是数字,都是先存储在内存中的一个缓冲区里面,叫做键盘缓冲区,简称输入缓冲区或者输入流

当一次键盘输入结束时会将输入的数据存入输入缓冲区,而cin函数直接从输入缓冲区中读取数据。这种缓冲机制规定,只有收到回车键时,才会将所有输入的数据一次性提交到cin函数。回车标志一次输入的完成,如果数据不够,则会等待用户继续输入;如果数据有多余,则将多余的数据存储在输入流缓冲区中,供下次使用。

————————————————
参考链接:https://blog.csdn.net/zhao708981169/article/details/36392681

如何解决

1.cin.clear()

在这里插入图片描述

这是各个状态标识符的含义:
在这里插入图片描述
(有道翻译)

  • 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()

但是!!!

并不是所有时候都有效,具体可以参考这篇博文:

———————————————— 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()

具体用法:

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())

本文来自CSDN:https://blog.csdn.net/qq_43393963/article/details/105562662

posted @ 2020-04-25 17:49  方格田  阅读(1327)  评论(0编辑  收藏  举报