《C++ Primer》學習筆記(八):標準 IO 庫
歡迎關注WX公眾號:【程式設計師管小亮】
專欄C++學習筆記
《C++ Primer》學習筆記/習題答案 總目錄
——————————————————————————————————————————————————————
文章目錄
?? Cpp-Prime5 + Cpp-Primer-Plus6 原始碼和課後題
標準 IO 庫
部分IO庫設施:
istream
:輸入流型別,提供輸入操作。ostream
:輸出流型別,提供輸出操作。cin
:istream
物件,從標準輸入讀取資料。cout
:ostream
物件,向標準輸出寫入資料。cerr
:ostream
物件,向標準錯誤寫入資料。>>
運算子:從istream
物件讀取輸入資料。<<
運算子:向ostream
物件寫入輸出資料。getline
函式:從istream
物件讀取一行資料,寫入string
物件。
1、IO類
標頭檔案 iostream 定義了用於讀寫流的基本型別,fstream 定義了讀寫命名檔案的型別,sstream 定義了讀寫記憶體中 string
物件的型別。
寬字元版本的IO型別和函式的名字以 w
開始,如 wcin
、wcout
和 wcerr
分別對應 cin
、cout
和cerr
。它們與其對應的普通 char
版本都定義在同一個標頭檔案中,如標頭檔案 fstream 定義了 ifstream
和 wifstream
型別。
通常可以將一個派生類(繼承類)物件當作其基類(所繼承的類)物件來使用,這是通過 繼承機制(inheritance) 實現的。
1)IO象無拷貝或賦值
不能拷貝或對IO物件賦值。
ofstream out1, out2;
out1 = out2; // 錯誤:不能對流物件賦值
ofstream print(ofstream); // 錯誤:不能初始化ofstream引數
out2 = print(out2); // 錯誤:不能拷貝流物件
由於IO物件不能拷貝,因此不能將函式形參或返回型別定義為流型別。進行IO操作的函式通常以引用方式傳遞和返回流。讀寫一個IO物件會改變其狀態,因此傳遞和返回的引用不能是 const
的。
2)條件狀態
IO操作一個與生俱來的問題是可能發生錯誤。一些錯誤是可恢復的,而其他錯誤則發生在系統深處,已經超出了應用程式可以修正的範圍。
IO庫條件狀態:
while (cin >> word)
// ok: 讀操作成功....
badbit
表示系統級錯誤,如不可恢復的讀寫錯誤。通常情況下,一旦 badbit
被置位,流就無法繼續使用了。在發生可恢復錯誤後,failbit
會被置位,如期望讀取數值卻讀出一個字元。如果到達檔案結束位置,eofbit
和 failbit
都會被置位。如果流未發生錯誤,則 goodbit
的值為0。如果 badbit
、failbit
和 eofbit
任何一個被置位,檢測流狀態的條件都會失敗。
good
函式在所有錯誤均未置位時返回 true
。而 bad
、fail
和 eof
函式在對應錯誤位被置位時返回 true
。此外,在 badbit
被置位時,fail
函式也會返回 true
。因此應該使用 good
或 fail
函式確定流的總體狀態,eof
和 bad
只能檢測特定錯誤。
流物件的 rdstate
成員返回一個 iostate
值,表示流的當前狀態。setstate
成員用於將指定條件置位(疊加原始流狀態)。clear
成員的無參版本清除所有錯誤標誌;含參版本接受一個 iostate
值,用於設定流的新狀態(覆蓋原始流狀態)。
// 記住cin的當前狀態
auto old_state = cin.rdstate(); // 記住cin的當前狀態
cin.clear(); // 使cin有效
process_input(cin); // 使用cin
cin.setstate(old_state); // 將cin置為原有狀態
3)管理輸出緩衝
每個輸出流都管理一個緩衝區,用於儲存程式讀寫的資料。
導致緩衝重新整理(即資料真正寫入輸出裝置或檔案)的原因有很多:
- 程式正常結束。
- 緩衝區已滿。
- 使用操縱符(如
endl
)顯式重新整理緩衝區。 - 在每個輸出操作之後,可以用
unitbuf
操縱符設定流的內部狀態,從而清空緩衝區。預設情況下,對cerr
是設定unitbuf
的,因此寫到cerr
的內容都是立即重新整理的。 - 一個輸出流可以被關聯到另一個流。這種情況下,當讀寫被關聯的流時,關聯到的流的緩衝區會被重新整理。預設情況下,
cin
和cerr
都關聯到cout
,因此,讀cin
或寫cerr
都會重新整理cout
的緩衝區。
flush
操縱符重新整理緩衝區,但不輸出任何額外字元。ends
向緩衝區插入一個空字元,然後重新整理緩衝區。
cout << "hi!" << endl; // 輸出hi和一個換行,然後重新整理緩衝區
cout << "hi!" << flush; // 輸出hi,然後重新整理緩衝區,不附加任何額外字元
cout << "hi!" << ends; // 輸出hi和一個空字元,然後重新整理緩衝區
如果想在每次輸出操作後都重新整理緩衝區,可以使用 unitbuf
操縱符。它令流在接下來的每次寫操作後都進行一次 flush
操作。而 nounitbuf
操縱符則使流恢復使用正常的緩衝區重新整理機制。
cout << unitbuf; // 所有輸出操作後都會立即重新整理緩衝區
// 任何輸出都立即重新整理,無緩衝
cout << nounitbuf; // 回到正常的緩衝方式
如果程式異常終止,輸出緩衝區是不會被重新整理的。當一個程式崩潰後, 它所輸出的資料很可能停留在輸出緩衝區中等待列印。
當除錯一個已經崩潰的程式時,需要確認那些你認為已經輸出的資料確實已經重新整理了。否則, 可能將大量時間浪費在追蹤程式碼為什麼沒有執行上,而實際上程式碼已經執行了,只是程式崩潰後緩衝區沒有被重新整理,輸出資料被掛起沒有列印而已。
當一個輸入流被關聯到一個輸出流時,任何試圖從輸入流讀取資料的操作都會先重新整理關聯的輸出流。標準庫將cout
和cin
關聯在一起,因此下面的語句會導致cout
的緩衝區被重新整理:
cin >> ival;
互動式系統通常應該關聯輸入流和輸出流。這意味著包括使用者提示資訊在內的所有輸出,都會在讀操作之前被列印出來。
使用 tie
函式可以關聯兩個流。它有兩個過載版本:無參版本返回指向輸出流的指標。如果本物件已關聯到一個輸出流,則返回的就是指向這個流的指標,否則返回空指標。tie
的第二個版本接受一個指向 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間的正常關聯
每個流同時最多關聯一個流,但多個流可以同時關聯同一個 ostream
。向 tie
傳遞空指標可以解開流的關聯。
2、檔案輸入輸出
標頭檔案 fstream 定義了三個型別來支援檔案IO:ifstream
從給定檔案讀取資料,ofstream
向指定檔案寫入資料,fstream
可以同時讀寫指定檔案。
1)使用檔案流物件
每個檔案流型別都定義了open
函式,它完成一些系統操作,定位指定檔案,並視情況開啟為讀或寫模式。
建立檔案流物件時,如果提供了檔名(可選),open
會被自動呼叫。
ifstream in(ifile); // 構造一個ifstream並開啟給定檔案
ofstream out; // 輸出檔案流未關聯到任何檔案
在C++11中,檔案流物件的檔名可以是 string
物件或C風格字元陣列。舊版本的標準庫只支援C風格字元陣列。
在要求使用基類物件的地方,可以用繼承型別的物件代替。因此一個接受 iostream
型別引用或指標引數的函式,可以用對應的 fstream
型別來呼叫。
可以先定義空檔案流物件,再呼叫 open
函式將其與指定檔案關聯。如果 open
呼叫失敗,failbit
會被置位。
一旦一個檔案流已經開啟,它就保持與對應檔案的關聯。對一個已經開啟的檔案流呼叫 open
會失敗,並導致 failbit
被置位。隨後試圖使用檔案流的操作都會失敗。如果想將檔案流關聯到另一個檔案,必須先呼叫 close
關閉當前檔案,再呼叫 clear
重置流的條件狀態(close
不會重置流的條件狀態)。
當
fstream
物件被銷燬時,close
會自動被呼叫。
2)檔案模式
每個流都有一個關聯的檔案模式,用來指出如何使用檔案。
指定檔案模式有如下限制:
- 只能對
ofstream
或fstream
物件設定out
模式。 - 只能對
ifstream
或fstream
物件設定in
模式。 - 只有當
out
被設定時才能設定trunc
模式。 - 只要
trunc
沒被設定,就能設定app
模式。在app
模式下,即使沒有設定out
模式,檔案也是以輸出方式開啟。 - 預設情況下,即使沒有設定
trunc
,以out
模式開啟的檔案也會被截斷。如果想保留以out
模式開啟的檔案內容,就必須同時設定app
模式,這會將資料追加寫到檔案末尾;或者同時設定in
模式,即同時進行讀寫操作。 ate
和binary
模式可用於任何型別的檔案流物件,並可以和其他任何模式組合使用。- 與
ifstream
物件關聯的檔案預設以in
模式開啟,與ofstream
物件關聯的檔案預設以out
模式開啟,與fstream
物件關聯的檔案預設以in
和out
模式開啟。
每個檔案流型別都定義了一個預設的檔案模式,當我們未指定檔案模式時,就使用此預設模式。與 ifstream
關聯的檔案預設以 in
模式開啟;與 ofstream
關聯的檔案預設以 out
模式開啟;與 fstream
關聯的檔案預設以 in
和 out
模式開啟。
預設情況下,開啟 ofstream
物件時,檔案內容會被丟棄,阻止一個 ofstream
清空給定檔案內容的方法是同時指定 app
模式:
流物件每次開啟檔案時都可以改變其檔案模式。
ofstream out; // 未指定檔案開啟模式
out.open("scratchpad"); // 模式隱含設定為輸出和截斷
out.close(); // 關閉out,以便我們將其用於其他檔案
out.open("precious", ofstream::app); // 模式為out和app
out.close();
保留被
ofstream
開啟的檔案中已有資料的唯一方法是顯式指定app
或in
模式。
3、string流
標頭檔案 sstream 定義了三個型別來支援記憶體IO:istringstream
從 string
讀取資料,ostringstream
向 string
寫入資料,stringstream
可以同時讀寫 string
的資料。
1)使用istringstream
當某些工作是對整行文字進行處理, 而其他一些工作是處理行內的單個單詞時,通常可以使用 istringstream
。
// 成員預設為公有
struct PersonInfo
{
string name;
vector<string> phones;
};
string line, word; // 分別儲存來自輸入的一行和單詞
vector<PersonInfo> people; // 儲存來自輸入的所有記錄
// 逐行從輸入讀取資料,直至cin遇到檔案尾(或其他錯誤)
while (getline(cin, line))
{
PersonInfo info; // 建立一個儲存此記錄資料的物件
istringstream record(line); // 將記錄繫結到剛讀入的行
record >> info.name; // 讀取名字
while (record >> word) // 讀取電話號碼
info.phones.push_back(word); // 保持它們
people.push_back(info); // 將此記錄追加到people末尾
}
2)使用ostringstream
當逐步構造輸出, 希望最後一起列印時, ostringstream
是很有用的。
for (const auto &entry : people)
{
// 對people中每一項
ostringstream formatted, badNums; // 每個迴圈步建立的物件
for (const auto &nums : entry.phones)
{
// 對每個數
if (!valid(nums))
{
badNums << " " << nums; // 將數的字串形式存入badNums
}
else
// 將格式化的字串"寫入"
formatted << " " << format(nums);
}
if (badNums.str().empty()) // 沒有錯誤的數
os << entry.name << " " // 列印名字
<< formatted.str() << endl; // 和格式化的數
else // 否則,列印名字和錯誤的數
cerr << "input error: " << entry.name
<< " invalid number(s) " << badNums.str() << endl;
}
參考文章
- 《C++ Primer》
相關文章
- C++學習筆記 — STL標準模板庫C++筆記
- Linux程式設計學習筆記 | Linux IO學習[2] – 標準IOLinux程式設計筆記
- Java IO學習筆記八:Netty入門Java筆記Netty
- C++ primer Plus學習筆記(第二章)C++筆記
- C++ Primer筆記C++筆記
- Mudo C++網路庫第八章學習筆記C++筆記
- 《C++ Primer》學習筆記(六):C++模組設計——函式C++筆記函式
- C++ Primer 第二章 學習筆記及習題答案C++筆記
- 【C++ Primer Plus】學習筆記--第10章 物件和類C++筆記物件
- 機器學習筆記——特徵標準化機器學習筆記特徵
- C++標準庫、C++標準模版庫介紹C++
- C標準庫學習
- C++ 學習筆記之——STL 庫 queueC++筆記
- C++ 學習筆記(3):引用和指標C++筆記指標
- C++標準庫C++
- C++ Primer Plus 隨記(第八章)C++
- C++ Primer Plus 第四章 複合型別 學習筆記C++型別筆記
- 《C++ Primer中文版(第5版)》學習筆記與習題完整發布!C++筆記
- freeRTOS核心學習筆記(1)-程式設計標準筆記程式設計
- 我的隨行筆記3 C++ Primer Plus 3--指標筆記C++指標
- 飛機的 PHP 學習筆記八:資料庫PHP筆記資料庫
- JavaScript學習筆記(八)—— 補JavaScript筆記
- C++標準庫:chronoC++
- C++標準庫:randomC++random
- 基礎 IO (Linux學習筆記)Linux筆記
- Java學習筆記09(IO流)Java筆記
- 資料庫學習筆記——20 使用遊標資料庫筆記
- Golang標準庫學習—container/heapGolangAI
- C++學習筆記——003C++筆記
- C++學習筆記——001C++筆記
- OI學習筆記(C++)筆記C++
- c++學習筆記(三)C++筆記
- c++學習筆記(五)C++筆記
- c++學習筆記(四)C++筆記
- HexMap學習筆記(八)——水體筆記
- hive學習筆記之八:SqoopHive筆記OOP
- 指標學習筆記指標筆記
- nodejs的socket.io學習筆記NodeJS筆記