1. 程式人生 > >C++Primer——《第八章 》IO 類

C++Primer——《第八章 》IO 類

目錄

IO 類

IO 物件無拷貝和賦值

條件狀態

查詢流的狀態

管理條件狀態

管理緩衝區物件

關聯輸入輸出流

檔案輸入輸出

使用檔案流物件

成員函式open 和 close

string 流



IO 類


● C++語言不直接處理輸入輸出,而是通過一組定義在標準庫中的型別來處理IO。 這些型別支援從裝置讀取資料向裝置寫入資料的IO操作, 裝置可以是檔案、控制檯視窗等。 還有一些型別允許記憶體IO,即, 從string 讀取資料, 向string寫入資料。

這裡寫圖片描述

● 我們已經使用過的 IO 型別和物件都是操縱 char 資料的。 iostream 定義了用於讀寫流的基本型別, fstream 定義了讀寫命名檔案的型別, sstream 定義了讀寫記憶體 string 物件的型別。

為了支援使用寬字元的語言,標準庫定義了一組型別和物件來操縱 wchar_t 型別的資料,寬字元版本的型別和函式的名字以一個 w 開始。wcin, wcout和wcerr是分別對應cin. cout和cerr的寬字元版物件。

注意: 寬字元版本的型別和物件與其對應的普通的char 版本的型別定義在同一個標頭檔案中。 例如: 標頭檔案fstream 定義了ifstream和wifstream 型別。
 


IO 物件無拷貝和賦值


注意: 不能拷貝和對IO物件賦值,也不能將形參和返回型別設定為流型別。 進行IO操作的函式通常以引用方式傳遞和返回流。 傳遞和返回的引用不能是const

的,因為會改變IO物件的狀態。

     ofstream out1, out2;
    out1 = out2; //錯誤,不能對流物件賦值
    ofstream print(ofstream);  //不能初始化ofstream 引數
    out2 = print(out2); //不能拷貝流物件

 


條件狀態


IO 操作一個與生俱來的問題是可能發生錯誤。一些錯誤是可恢復的,  而其他錯誤則 發生在系統深處,已經超出了應用程式可以修正的範圍。下圖列出了 IO 類所定義的一些函式和標誌,可以幫助我們訪問和操縱流的條件狀態:

這裡寫圖片描述

這裡寫圖片描述

 

● 一個流一旦發生錯誤,其上後續的 IO 操作都會失敗。由於流可能處於錯誤狀態,因此程式碼通常應該在使用一個流之前檢查它是否處於良好狀態。

確定一個流物件的狀態最簡單的方法是將它當作一個條件來使用:

while(cin>>word)

while 迴圈檢查”>>” 表示式返回的流的狀態。 如果輸入操作成功,流保持有效狀態,則條件為真。
 


查詢流的狀態


● IO 庫定義了一個與機器無關的 iostate 型別,它提供了表達流狀態的完整功能。badbit 表示系統級錯誤,如不可恢復的讀寫錯誤。 在發生可恢復錯誤後,failbit 被置位,如期望讀取數值卻讀出一個字元等錯誤。

如果 badit、failbit 和 eofbit 任一個被置位,則檢測流狀態的條件會失敗。*使用good和fail是確定流的總體狀態的正確方法。* 實際上,我們將流當作條件使用的程式碼就等價於 !fail( )。而 eof 和 bad 操作只能表示特定的錯誤。


 


管理條件狀態


● 流物件的 rdstate 成員返回一個 iostate 值,對應流的當前狀態。setstate 操作將給定條件位置位,表示發生了對應錯誤。

clear 不接受引數的版本清除(復位)所以錯誤標誌位。執行 clear( ) 後,呼叫 good 會返回 true。帶引數的 clear 版本接受一個 iostate 值,表示流的新狀態。

auto old_state = cin.rdstate(); //記住cin 的當前狀態
cin.clear(); //是cin有效

process_input(cin);  //一個函式使用cin
cin.setstate(old_state); //將cin置為原有狀態


 


管理緩衝區物件


os<<"please enter a value: "; //文字串可能立即打印出來,但也有可能被作業系統儲存在緩衝區中,隨後在列印

● 每個輸出流都管理一個緩衝區,用來儲存程式讀寫的資料。有了緩衝機制,作業系統就可以將程式的多個輸出操作組合成單一的系統級寫操作。由於裝置的寫操作可能很耗時,允許作業系統將多個輸出操作組合為單一的裝置寫操作可以帶來很大的效能提升。
這裡寫圖片描述

 

● endl 完成換行並重新整理緩衝區的工作;flush 重新整理緩衝區,但不輸出任何額外的字元;ends 向緩衝區插入一個空字元,然後重新整理緩衝區。

● 如果想在每次輸出操作後都重新整理緩衝區, 我們可以使用unitbuf 操縱符。它告訴流在接下來的每次寫操作之後都進行一次flush操作。 而nounitbuf 操縱符則重置流, 使其恢復使用正確的系統管理的緩衝區重新整理機制:

cout<<unitbuf;  // 所有輸出操作後都會立即重新整理緩衝區
// 任何輸出都立即重新整理,無緩衝
cout<<nounitbuf; //回到正常的緩衝方式

這裡寫圖片描述

 


關聯輸入輸出流


● 當一個輸入流被關聯到一個輸出流時,任何試圖從輸入流讀取資料的操作都會先重新整理關聯的輸出流。標準庫將cout和cin關聯在一起:

cin>>ival; // 導致cout的緩衝區被重新整理

● 我們既可以將一個istream 物件關聯到另一個ostream,也可以將一個ostream 關聯到另一個ostream

cin.tie(&cout); // 僅僅用來展示:標準庫將 cin 和 cout 關聯在一起
//old_tie 指向當前關聯到cin的流(如果有的話)
ostream *old_tie = cin.tie(nullptr); //cin 不再與其他流關聯

// 將cin 和 cerr 關聯, 這不是一個好的主意, 因為cin應該關聯到cout
cin.tie(&cerr); //讀取cin 會重新整理cerr 而不是cout
cin.tie(old_tie); // 重建cin 和 cout 間的正常關聯

在這段程式碼中, 為了將一個給定的流關聯到一個新的輸出流, 我們將流的指標傳遞給了tie。為了徹底解開流的關聯, 我們傳遞了一個空指標。 每個流同時最多關聯到一個流,但多個流可以同時關聯到同一個 ostream 。

 


檔案輸入輸出


● 標頭檔案 fstream 定義了三個型別來支援檔案 IO : ifstream 從一個給定檔案讀取資料,ofstream 向一個給定檔案寫入資料,以及 fstream 可以讀寫給定檔案。

 

● 這些型別提供的操作與我們之前已經使用過的物件cin 和cout 的操作一樣。 我們還可以用IO 運算子(<< 和 >>)來讀寫檔案, 可以用getline 從一個ifstream 讀取資料。
 

這裡寫圖片描述

 


使用檔案流物件


● 當我們想要讀寫一個檔案時,可以定義一個檔案流物件, 並將物件與檔案關聯起來。

每個檔案流類都定義了一個名為open 的成員函式, 它完成一些系統相關的操作, 來定位給定的檔案, 並視情況開啟為讀或寫模式。

在建立檔案流物件時, 我們可以提供檔名(可選的)。 如果提供了一個檔名, 則open 會自動被呼叫:

ifstream in(ifile);  // 構造一個ifstream 並開啟給定檔案
ofstream out;  // 輸出檔案流未關聯到任何檔案

● 程式碼定義了一個輸入流in,它被初始化為從檔案讀取資料,檔名由string 型別的引數ifile指定。 第二條語句定義了一個輸出流out ,未與任何檔案關聯。

C++11 標準中,檔名既可以是庫型別 string 物件,也可以是 C 風格字元陣列,而舊版本的標準庫只允許 C 風格字元陣列。
 


成員函式open 和 close


● 如果我們定義了一個空檔案流物件, 可以隨後呼叫****open來將它與檔案關聯起來:

ifstream in(ifile); //構築一個ifstream並開啟給定檔案
ofstream out; //輸出檔案流未與任何檔案相關聯
out.open(ifile + ".copy"); // 開啟指定檔案

如果呼叫open失敗, failbit 會被置位, 因為呼叫open可能失敗, 進行open是否成功的檢測通常是一個好習慣:
 

if(out) //檢查open 是否成功

 // open 成功,我們可以使用檔案了


如果open失敗, 條件會為假,我們就不能使用out了。

● 一旦一個檔案流已經開啟, 它就保持與對應檔案的關聯。注意: 對一個已經開啟的檔案流呼叫 open 會失敗, 並會導致 failbit 被置位。 隨後的試圖使用檔案流的操作都會失敗。 為了將檔案流關聯到另外一個檔案, 必須首先關閉已經關聯的檔案。 一單檔案成功關閉, 我們就可以開啟新的檔案:

in.close(); //關閉檔案
in.open(ifile + “2”); // 開啟另一個檔案

如果open 成功,則open 會設定流的狀態,使得good() 為 true 。
 


string 流


● istringstream 從 string 讀取資料,ostringstream 向 string 寫入資料,而標頭檔案 stringstream 既可從 string 讀資料也可向 string 寫資料。

● 當我們的某些工作是對整行文字進行處理,而其他一些工作是處理行內的單個單詞時,通常可以使用 istringstream 。

● 當我們逐步構造輸出,希望最後一起列印時,ostringstream 是很有用的。

● C++ 使用標準庫類來處理面向流的輸入與輸出:
 

iostream 處理控制檯 IO

fstream 處理命名檔案 IO

stringstream 完成記憶體 string 的 IO

每個 IO 物件都維護一組條件狀態,用來指出此物件上是否可以進行 IO 操作。