c++中的基本IO

HickeyZhang發表於2021-07-22

引言

c++不直接處理輸入和輸出,而是通過標準庫中的型別處理IO。IO的裝置可以是檔案、控制檯、string。c++主要定義了三種IO型別,分別被包含在iostreamfstreamsstream標頭檔案中。

為了支援使用寬字元的語言,標準庫定義了一組型別和物件操縱wchar_t型別的資料。

以下是這三種IO庫型別以及標頭檔案:

  • iostream標頭檔案
    • istream(寬字元版本wistream),從流讀取資料。
    • ostream (寬字元版本wostream),向流寫入資料。
    • iostream(寬字元版本wiostream),讀寫流。
  • fstream標頭檔案
    • ifstream(寬字元版本wifstream),從檔案讀取資料。
    • ofstream (寬字元版本wofstream),向檔案寫入資料。
    • fstream(寬字元版本wfstream),讀寫檔案。
  • sstream標頭檔案
    • istringstream(寬字元版本wistringstream),從string讀取資料。
    • ostringstream (寬字元版本wostringstream),向string寫入資料。
    • stringstream(寬字元版本wstringstream),讀寫string。

裝置型別和字元大小不會影響我們要執行的IO操作。得益於繼承機制,以上型別都可以使用>><<運算子以及getline()函式。

IO型別的通用特性

IO物件不能拷貝或賦值

istream is1,is2;
is1 = is2; //錯誤,流物件不能賦值
istream is3(is1); //錯誤,流物件不能拷貝

由於無法拷貝IO物件,因此不能將形參或返回型別設定為流型別。通常使用引用方式傳遞和返回流。但傳遞和返回的引用不能是const的,因為讀寫IO物件會改變IO物件的狀態,也就是改變了IO物件。

IO物件的狀態資訊

IO類定義了一些函式和標誌位,幫助我們檢查和操縱流的狀態:

  • strm::iostate是一種型別,這種型別就像一串二進位制位串,每個二進位制位串指出了流的狀態。(strm為引言中的任意一種IO型別)。
  • eof()函式用於在IO物件上呼叫,如cin.eof()。如果流檢測到eof(檔案結束標誌),該函式返回true。
  • fail()函式使用同上。如果流處於崩潰或IO操作失敗的狀態,返回true。
  • bad()函式使用同上。如果流處於崩潰狀態,返回true。
  • good()函式使用同上。如果流處於有效狀態,返回true。
  • clear()函式使用同上。將IO物件中的所有條件狀態為復位,流的狀態變為有效狀態,返回void。
  • setstate(flags),將IO物件的狀態為按flags(型別為strm::iostate)指示的那樣置位。
  • rdstate()函式用於在IO物件上呼叫,讀取IO物件的狀態位,返回型別為strm::iostate

一旦流發生錯誤,這個流上的後續IO操作都會失敗,因此最好在使用流之前檢查它是否處於良好狀態。如

// 如果輸入成功,流保持有效狀態,條件為真
while(cin >> word){
    //讀操作成功,其他操作。
}

輸出緩衝

所有輸出流都管理一個緩衝區,用來儲存程式讀寫的資料。

    cout << "Hello World!";

串"Hello World!"可能被立即列印出來,也可能被作業系統儲存在緩衝區,隨後列印。

以下原因可以重新整理緩衝(即真正將資料輸出到目標裝置或檔案中):

  • 程式正常結束,自動重新整理。
  • 緩衝區滿時,自動重新整理。
  • 可以使用操作符endlflushends手動重新整理緩衝區(只作用一次輸出)。
    cout << "1" << endl; //字串後新增換行,然後重新整理緩衝區
    cout << "2" << flush; //僅重新整理緩衝區
    cout << "3" << ends;  //字串後新增一個空字元,然後重新整理緩衝區。
  • 通過操作符unitbuf設定自動重新整理。不同於endlflushends只作用於一次輸出,設定了unitbuf後的輸出流每輸出一次都會自動重新整理緩衝區。
    cout << unitbuf;//下面的語句每執行一次輸出,就重新整理一次緩衝區。
    cout << "1"; //輸出"1",自動重新整理緩衝區
    cout << "2"; //輸出"2",自動重新整理緩衝區
    cout << "3"; //輸出"3",自動重新整理緩衝區
    cout << "4"; //輸出"4",自動重新整理緩衝區
    ...
    cout << "nounitbuf"; //回到流預設的緩衝方式
  • 關聯流。讀寫被關聯的流時,關聯到的流的緩衝區會被重新整理(tie()函式括號裡面的是關聯到的流,呼叫tie()的流是被關聯的流)。cout 和 cin預設關聯在一起,使用cin讀取資料時,cout的緩衝區被重新整理。
    cout << "Fuck you!"; //沒有指定操作符,cout預設不重新整理,該語句執行完後"Fuck you!"可能立即被輸出到螢幕,也可能稍後被輸出。
    int i;
    cin >> i; //cout的緩衝區被重新整理,此時"Fuck you!"一定已經真正輸出(可能在之前就已經真正輸出,此時重新整理緩衝區等於什麼都沒做)。

使用tie()函式關聯流和解除關聯:

    cin.tie(&cout); //有引數的tie(),引數為指向流的指標,且指標不為空,此時建立關聯。
    cin.tie(nullptr); //有引數的tie(),且指標為空,此時解除cin和其他流的關聯。
    cin.tie(); //無參tie(),返回指向cin當前關聯到的流的指標。

Note:

若程式崩潰即異常終止,輸出緩衝區不會被重新整理,換言之,緩衝區中的資料可能並沒有真正被輸出到檔案或裝置。

檔案IO

建立檔案流

前面所過,所有IO型別都可以使用>><<getline(),除此之外,檔案IO還有一些特有的操作。

建立檔案流:

    fstream fstrm1; //建立未繫結檔案的檔案流
    fstream fstrm2(s1); //建立繫結到指定檔案s1的檔案流(自動呼叫open())。s1是string或指向c風格字串的指標。
    //fstream fstrm3(s2, mode); 與第二條語句類似,但指定開啟檔案的模式。

Note:

當一個fstream的作用域內的程式碼執行完畢,fstream關聯的檔案被自動關閉,即fstream物件被銷燬時,close()會自動呼叫。

open和close

使用open開啟檔案,close關閉檔案。對一個已經開啟的檔案呼叫open會失敗,並且failbit被置位。

    string file1 = "qq.dat";
    ifstream ifs;
    ifs.open(file1);
    //讀取操作
    ifs.close();

檔案模式mode

常用的檔案模式mode如下:

  • in 只讀方式開啟檔案
  • out 以寫方式開啟檔案
  • app 每次寫操作在檔案末尾進行
  • ate 開啟檔案後立即定位到檔案末尾
  • trunc 截斷檔案,即輸出會覆蓋檔案中的原有資料。
  • binary 以二進位制方式開啟檔案

同時指定多個模式時使用|分隔:

    ofstream ofs("file1", ofstream::out | ofstream::app);

Note:

out模式隱含trunc即覆蓋原檔案,若要在原檔案末尾新增資料,則需要顯式指明app模式。

若沒有指定任何模式,則使用預設模式。

string IO

stringstream獨有的操作如下:

    sstream strm;   //sstream為sstream標頭檔案中定義的型別,具體可以是istringstream等。
    sstream strm(s); //建立一個sstream物件,儲存字串s的一個拷貝。
    strm.str(); //返回strm儲存的string的拷貝。
    strm.str(s); //將string s拷貝到strm。

宣告:
c++ Basic是對《C++ Primer 第五版》的個人總結與疑難解釋,主要用於個人日後複習。
如果想要深入瞭解更多,請支援正版。

相關文章