初識io流條件狀態

一  流狀態
   C++中的輸入輸出系統負責記錄每一個輸入輸出操作的結果信息,這些當前的狀態信息被包含在io_state類型的對象中。io_state是一個枚舉類型(就像open_mode一樣),以下便是它包含的值。  
eofbit 已到達文件尾 
failbit 非致命的輸入/輸出錯誤,可挽回 
badbit 致命的輸入/輸出錯誤,無法挽回

這三個標誌位均用一位二進制位來表示,0表示清除,1表示設置。

對於eofbit,failbit,badbit。0表示正常,1表示被設置。

這三個整體構成了流的狀態。若三個均爲0,表示流狀態正常,反之,若有一個爲1,流狀態非正常。包括達到了文件尾,出現可恢復性錯誤,出現了不可恢復性的錯誤。

另外還有一個標誌位goodbit用來測試流狀態是否正常。如果流狀態正常返回true,輸出1.否則輸出0。
相對應與這四個標誌位有四個函數分別用來測試相應的標誌位的狀態。他們分別爲:

bool bad();

bool eof();

bool fail();

bool good();

看上去這四個函數的返回值是bool類型,返回值應該是true和false,也就是在我們C++裏,0代表false,非0代表true,但是,在我們輸出他們的結果時,得到的是0和1.不是true和false,也不是0和非0值。

二 測試流狀態。

對於流狀態的測試我們有三種方法。

1 rdstate()函數

我們可以用這個函數來返回當前的流狀態。若我們用該函數輸出當前流的狀態信息,他輸出的並不是一個二進制數。他輸出的是一個十進制。是將二進制轉換爲對應的十進制後輸出的。我們可以寫個小程序進行一下測試,我們可以測試輸入流的狀態,也可以測試輸出流的狀態。

如:

#include <iostream>
using namespace std;
int main()
{
    cout << "hello" << endl;
    cout << cout.rdstate() << endl;//輸出當前的流狀態
    if(cout.rdstate() == ios::goodbit) 
    { 
     cout<<"輸出數據的類型正確,無錯誤!"<<endl; 
    } 
    if(cout.rdstate() == ios_base::failbit) 
    { 
     cout<<"輸出數據類型錯誤,非致命錯誤,可清除輸入緩衝區挽回!"<<endl;
    } 
    system("pause"); 
    return 0;
}

輸出的當前的流狀態爲0.    表示當前流狀態正常,那麼他就和我們的if條件判斷語句中的    第一個相匹配了。由此可知,rdstate() 返回的是eofbit,failbit,badbit三個標誌的值,即返回的是一個strm::iostate類型。

2 我們可以用三個標誌位對應的函數來測試當前的流狀態。

如:

#include <iostream>
using namespace std;
int main()
{
 cout << "你好嗎?" << endl;
    cout <<  cout.eof() << cout.fail() << cout.bad() << endl; 
    cout << cout.good() << endl;
    return 0;
}

輸出結果爲:

你好嗎?

000

1

前面的000表示三個標誌位對應的值,均爲0,由此可知此時流狀態正常,故goodbit標誌位被設置。所以good()函數輸出結果爲1.    

3 用流本身來測試

如: 

if (cin)

 while (cin >> word)

if語句直接檢查流的狀態,while語句則檢測表達式的返回值,即間接的檢查流狀態。

我們可以用如下的函數進行測試:

#include <iostream>
using namespace std;
int main()
{
 int i;
 cin >> i;
 if (cin)
 {
  cout << "流狀態正常" << i << endl;
 }
 else
 {
  cout << "流狀態不正常" << i << endl;
 }
    return 0;
}

當我們輸入的值爲整型值或是能隱式轉換成整型值時,當前輸入流狀態有效,若我們輸入了字符串類型值,則會使當前輸入流狀態無效。

同樣我們也可以對while語句進行測試。

#include <iostream>
using namespace std;
int main()
{
 int i;
 
 while(cin >> i)
 {
  cout << "當前輸入流狀態有效" << endl;
 }
 cout << "輸入流狀態無效" << endl;
 
    return 0;
}

當我們輸入的值爲整型值或是能隱式轉換成整型值時,當前輸入流狀態有效,若我們輸入了字符串類型值,則會使當前輸入流狀態無效。

三 表示流狀態的四個常量

標記位常量

常量

含義

failbit標記位的值

eofbit標記位的值

badbit標記位的值

轉化爲10進制

ios::failbit

輸入(輸出)流出現非致命錯誤,可挽回

1

0

0

4

ios::badbit

輸入(輸出)流出現致命錯誤,不可挽回

0

0

1

2

ios::eofbit

已經到達文件尾

0

1

0

1

ios::goodbit

流狀態完全正常

0

0

0

0

  

下面來解釋這張表格:
ios::failbit    ios::badbit    ios::eofbit    ios::goodbit均爲常量,它們任何一個都代表了一種流狀態,因此稱爲“輸入狀態標記位常量”。
比如,ios::failbit表示的是流狀態爲:
 流的failbit標記位值爲1,eofbit標記位值爲0,badbit標記位的值爲0。
         
始終牢記:failbit,badbit,eofbit組成了流狀態
        
注意:它們不是failbit、badbit、eofbit、goodbit這四個標記位的存貯變量。
這四個常量我認爲是strm::iostate類型的常量值。
分別對應於:
ios::failbit 010  ---2
ios::badbit  100  ---4
ios::eofbit  001  ---1
ios::goodbit 000  ---0
我們可以用函數來測試這三個常量的值:
#include <iostream>
using namespace std;
int main()
{
    cout << ios:: failbit << endl;
    cout << ios:: badbit << endl; 
    cout << ios:: eofbit << endl;
    cout << ios:: goodbit << endl;
    return 0;
}
由此我們也可以得出表示流狀態的三個標誌位的順序爲:
badbit,failbit,eofbit
注意: ios::failbit ,ios::badbit,ios::eofbit,ios::goodbit均爲常量值,它與當前的流狀態無關。

 

四 設置流狀態

1 設置流狀態函數我們可以用setstate(flag)函數.參數爲一個strm::iostate類型的值。

如: cin.setstate(ios::eofbit);

注意,它的目的並不是將流狀態ios::eofbit替換(或覆蓋)當前的輸入流狀態。而是僅僅的將標誌位eofbit置1,其他位保持不變。

好的,既然設置好了。我們可以來查看一下我們的流狀態是否設置成功了:

#include <iostream>
using namespace std;
int main()
{
 cout << "輸入流設置前的狀態: " << endl;
 cout << cin.bad() << cin.fail() << cin.eof() << endl;
 cin.setstate(ios::failbit);
 cout << "輸入流設置後的狀態: " << endl;
 cout << cin.bad() << cin.fail() << cin.eof() << endl;
    return 0;
}

然後我們再進行一下測試,看一下函數setstate是否覆蓋我們原來的狀態:

我們還用上一個函數,加點語句。

#include <iostream>
using namespace std;
int main()
{
 cout << "輸入流設置前的狀態: " << endl;
 cout << cin.bad() << cin.fail() << cin.eof() << endl;
 cin.setstate(ios::failbit);
 cout << "輸入流設置第一次後的狀態: " << endl;
 cout << cin.bad() << cin.fail() << cin.eof() << endl;
 cin.setstate(ios::badbit);
 cout << "輸入流設置第二次後的狀態: " << endl;
 cout << cin.bad() << cin.fail() << cin.eof() << endl;
    return 0;
}

輸出結果爲:

000

010

110

由此可知,函數setstate函數並沒有覆蓋我們原來的流狀態,而是僅僅的設置了對應位。

2 當我們設置了ios::failbit,ios::eofbit,ios::badbit流狀態後,由於該三種狀態爲非正常狀態,故設置後,對應的輸入流或是輸出流就處於無效狀態,我們此後對於該流的任何使用均爲無效,不起作用。

不妨我們來測試一下:

#include <iostream>
using namespace std;
int main()
{
 cin.setstate(ios::failbit);
 int i;
 cin >> i;
 cout << "hello!" << endl;
    return 0;
}

上面這個程序不等待我們輸入直接就輸出了結果hello!由此可知,cin狀態在設置後已處於無效狀態,所有對此的操作均爲視爲透明的,或是無效的,直接跳過。那我們如何將流狀態重置爲有效呢,我認爲有三種方法:

①:我們可以用函數clear();將流的所有狀態重設爲有效狀態。

②:我們可以用函數clear(flag);

flag 可選值有ios::badbit ios::failbit ios::eofbit,這裏參數的意思是重設對應的標誌位有效。

③:我們可以用函數setstate(ios::goodbit);重設流有效狀態

但經我在VC++ 6.0,VC++ 2008上測試後兩種方法雖能成功運行,但並沒起到作用。第一種方法可以。

#include <iostream>
using namespace std;
int main()
{
 cin.setstate(ios::failbit);

 cin.clear();
 int i;
 cin >> i;
 cout << "hello!" << endl;
    return 0;
}

輸入流重爲有效狀態

此題的while循環的判斷調件是,只要沒有遇到文件結束符,不管輸入流是否有效均執行循環體。

 

。下面我們再來整體看一個函數:

#include <iostream>
using namespace std;
int main()
{
 int i;
 while (cin >> i,!cin.eof())
 {
   try
   {
  if (cin.bad())  //出現系統故障
  {
   throw runtime_error("輸入輸出流出現故障");  //拋出異常
  }
   }
   catch(runtime_error err) //處理異常
   {
    cout << err.what() << endl; //輸出異常原因
    cout << "try again?y/n" << endl;
    char c;
    cin >> c;
    if ('y' == c)
    {
     continue;
    }
    else
    {
     break;
    }
    
   }
  if (cin.fail()) //出現了可恢復錯誤
  {
   cerr << "bad data, try again";
   cin.clear(); //重置輸入流有效
   cin.ignore(20,' '); //忽略20個輸入字符。若不寫此語句將陷入死循環。
   continue;
  }
  cout << i << " ";
 }
    return 0;
}

此題的while循環的判斷調件是,只要沒有遇到文件結束符,不管輸入流是否有效均執行循環體。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章