C++ 处理输入输出错误
處理輸入輸出時,我們必須預計到其中可能發(fā)生的錯誤并給出相應的處理措施。
當我們輸入時,可能會由于人的失誤(錯誤理解了指令、打字錯誤等)、文件格式不符、錯誤估計了情況等原因造成讀取失敗。當我們輸出時,如果輸出設備不可用、隊列滿或者發(fā)生了故障等,都會導致寫入失敗。發(fā)生輸入輸出錯誤的可能情況是無限的!但 C++ 將所有可能的情況歸結為四類,稱為流狀態(tài)(stream state)。每種流狀態(tài)都用一個 iostate 類型的標志位來表示。
流狀態(tài)對應的標志位
| badbit | 發(fā)生了(或許是物理上的)致命性錯誤,流將不能繼續(xù)使用。 |
| eofbit | 輸入結束(文件流的物理結束或用戶結束了控制臺流輸入,例如用戶按下了 Ctrl+Z 或 Ctrl+D 組合鍵。 |
| failbit | I/O 操作失敗,主要原因是非法數(shù)據(jù)(例如,試圖讀取數(shù)字時遇到字母)。流可以繼續(xù)使用,但會設置 failbit 標志。 |
| goodbit | 一切止常,沒有錯誤發(fā)生,也沒有輸入結束。 |
ios_base 類定義了以上四個標志位以及 iostate 類型,但是 ios 類又派生自 ios_base 類,所以可以使用 ios::failbit 代替 ios_base::failbit 以節(jié)省輸入。
一旦流發(fā)生錯誤,對應的標志位就會被設置,我們可以通過下表列出的函數(shù)檢測流狀態(tài)。
C++流狀態(tài)檢測函數(shù)及其說明
fail() 和 bad() 之間的區(qū)別并未被準確定義。
但是,基本的思想很簡單:
如果輸入操作遇到一個簡單的格式錯誤,則使流進入 fail() 狀態(tài),也就是假定我們(輸入操作的用戶)可以從錯誤中恢復。如果錯誤真的非常嚴重,例如發(fā)生了磁盤故障,輸入操作會使得流進入 bad() 狀態(tài)。也就是假定面對這種情況你所能做的很有限,只能退出輸入。以上觀點導致如下邏輯:
int i = 0; cin >> i; if(!cin){ //只有輸入操作失敗,才會跳轉到這里if(cin.bad()){ //流發(fā)生嚴重故障,只能退出函數(shù)error("cin is bad!"); //error是自定義函數(shù),它拋出異常,并給出提示信息}if(cin.eof()){ //檢測是否讀取結束//TODO:}if(cin.fail()){ //流遇到了一些意外情況cin.clear(); //清除/恢復流狀態(tài)//TODO:} }!cin 可以理解為“cin 不成功”或者“cin 發(fā)生了某些錯誤”或者“ cin 的狀態(tài)不是 good()”, 這與“操作成功”正好相反。
我們在處理 fail() 時所使用的 cin.clear()。當流發(fā)生錯誤時,我們可以進行錯誤恢復。為了恢復錯誤,我們顯式地將流從 fail() 狀態(tài)轉移到其他狀態(tài),從而可以繼續(xù)從中讀取字符。clear() 就起到這樣的作用——執(zhí)行 cin.clear() 后,cin 的狀態(tài)就變?yōu)?good()。
假定我們要讀取一個整數(shù)序列并存入 vector 中,字符*或“文件尾”表示序列結束。Windows 平臺按下 Ctrl+Z 組合鍵,再按下回車鍵表示到達文件末尾;類Unix系統(tǒng)按下 Ctrl+D 組合鍵表示到達文件末尾。
上述功能可通過如下函數(shù)來實現(xiàn):
//從 ist 中讀入整數(shù)到 v 中,直到遇到 eof() 或終結符 void fill_vector(istream& ist, vector<int>& v, char terminator){for( int i; ist>>i; ) v.push_back(i);//正常情況if(ist.eof()) return; //發(fā)現(xiàn)到了文件尾,正確,返回//發(fā)生嚴重錯誤,只能退出函數(shù)if (ist.bad()){error("cin is bad!"); //error是自定義函數(shù),它拋出異常,并給出提示信息}//發(fā)生意外情況if (ist.fail()) { //最好清除混亂,然后匯報問題ist.clear(); //清除流狀態(tài)//檢測下一個字符是否是終結符char c;ist>>c; //讀入一個符號,希望是終結符if(c != terminator) { // 非終結符ist.unget(); //放回該符號ist.clear(ios_base::failbit); //將流狀態(tài)設置為 fail()}} }如果發(fā)生了 fail(),我們嘗試檢測下一個字符是否是結束符:如果是,那么就完整得讀取了數(shù)據(jù),使用 clear() 恢復狀態(tài)就可以;如果不是,我們就沒有辦法處理了,所以將狀態(tài)重新設置為 fail(),以期望 fill_vector() 的調用者(上層函數(shù))有能力處理。
我們通過調用 ist.clear(ios_base::failbit) 來將流狀態(tài)設置為 fail()。
可以用 unget() 將字符放回 ist,以便 fill_vector() 的調用者可能使用該字符。
如果 fill_vector() 的調用者想知道是什么原因終止了輸入,那么可以檢測流是處于 fail() 還是 eof() 狀態(tài)。當然也可以捕獲 error() 拋出的 runtime_error 異常,但當 istream 處于 bad() 狀態(tài)時,繼續(xù)獲取數(shù)據(jù)是不可能的。
簡單起見,可以讓 istream 幫我們拋出這個異常。
//當 ist 出現(xiàn)問題時拋出異常 ist.exceptions(ist.exceptions() | ios_base:: badbit);當語句執(zhí)行時,如果 ist 處于 bad() 狀態(tài),它會拋出一個標準庫異常 ios_base::failure。在一個程序中,我們只需要調用 exceptions() 一次。這允許我們簡化關聯(lián)于 ist 的所有輸入過程,同時忽略對 bad() 的處理:
//從ist中讀入整數(shù)到v中,直到遇到eof()或終結符 void fill_vector(istream& ist, vector<int>& v, char terminator){ist.exceptions(ist.exceptions() | ios_base:: badbit);for (int i; ist>>i; ) v.push_back(i);if (ist.eof()) return; //發(fā)現(xiàn)到了文件尾//不是good(),不是bad(),不是eof(),ist的狀態(tài)一定是fail()ist.clear(); //清除流狀態(tài)char c;ist>>c; //讀入一個符號,希望是終結符if (c != terminator) { //不是終結符號,一定是失敗了ist.unget(); //也許程序調用者可以使用這個符號ist.clear(ios_base::failbit); //將流狀態(tài)設置為 fail()} }這里使用了 ios_base,它是 iostream 的一部分,包含了對常量如 badbit 的定義、異常如 failure 的定義,以及其他一些有用的定義。可以通過::操作符來使用它們,例如 ios_ base::badbit。
與 istream—樣,ostream 也有四個狀態(tài):good()、fail()、eof() 和 bad()。
總結
以上是生活随笔為你收集整理的C++ 处理输入输出错误的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Spring Boot 返回XML
- 下一篇: C++ 输出单个字符