問題引入
用過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())
本文來自CSDN:https://blog.csdn.net/qq_43393963/article/details/105562662