在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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM