1 C++緩沖區(qū)
在學習標準IO庫之前,我們先了解C++中緩沖區(qū)的使用。關(guān)于操作系統(tǒng)中緩沖區(qū)的學習與理解,請查看操作系統(tǒng)-緩存管理。
1.1 什么是緩沖區(qū)
緩沖區(qū)又稱緩存,是內(nèi)存空間的一部分。系統(tǒng)在內(nèi)存中預(yù)留一部分用于緩沖輸入輸出的數(shù)據(jù),緩沖區(qū)根據(jù)用途分為輸入緩沖區(qū)和輸出緩沖區(qū)兩種。
1.2 為什么要引入緩沖區(qū)
C++中緩沖區(qū)的使用主要有以下幾個目的:
- 緩和CPU與I/O設(shè)備間速度不匹配的矛盾。
- 減少對CPU的中斷頻率,放寬對CPU中斷響應(yīng)時間的限制。
- 提高CPU和I/O設(shè)備之間的并行性,讓CPU可以處理其他工作
1.3 緩沖區(qū)類型
C++緩沖區(qū)分為全緩沖、行緩沖和無緩沖三種。
全緩沖
在此類緩沖中,當填滿標準I/O緩沖后,才進行實際的I/O操作。典型的代表是磁盤文件的讀寫。行緩沖
在此類緩沖中,當在輸入和輸出中遇到換行符時,執(zhí)行真正的I/O操作。輸入的字符先存放在緩沖區(qū),等按下回車鍵換行時才進行實際的I/O操作。典型代表是鍵盤輸入數(shù)據(jù)。無緩沖
標準出錯情況stderr是典型代表,這使得出錯信息可以直接盡快地顯示出來。
1.4 緩沖區(qū)的刷新
下列情況將導(dǎo)致緩沖區(qū)刷新
程序正常結(jié)束
作為main函數(shù)return的一部分,緩沖刷新被執(zhí)行。緩沖區(qū)滿
需要刷新緩沖,新數(shù)據(jù)才可寫入。使用操作符控制緩沖區(qū)刷新
endl:輸出內(nèi)容加換行,然后刷新
ends:輸出內(nèi)容加空字符,然后刷新
flush:輸出內(nèi)容后直接刷新用
unitbuf設(shè)置流的內(nèi)部狀態(tài)
cout << unitbuf; //所有輸出操作后會立即刷新緩沖區(qū)
cout << <<nounitbuf; //回到正常的緩沖方式
- 一個輸出流被關(guān)聯(lián)到另一個流上
使用tie函數(shù)可以將兩個流關(guān)聯(lián)在一起。當讀寫被關(guān)聯(lián)的流,被關(guān)聯(lián)的流會刷新。cin和cerr都關(guān)聯(lián)到cout,因此讀cin或者cerr都會導(dǎo)致cout的緩沖區(qū)更新。我們可以將istream關(guān)聯(lián)到ostream上,也可將ostream關(guān)聯(lián)到另一個ostream上。不建議將cin關(guān)聯(lián)到cerr上。
警告:如果程序崩潰了,則不會刷新緩沖區(qū)。
1.5 行緩沖演示
getchar()是行緩沖的,第一次調(diào)用getchar()函數(shù),會讓程序使用者輸入一行字符并直至按下回車鍵函數(shù)才返回。此時用戶輸入的字符和回車符都存放在行緩沖區(qū)。再次調(diào)用getchar()函數(shù),會逐步輸出行緩沖區(qū)的內(nèi)容。
int main(){
char c;
c = getchar();
cout << c << endl;
while ((c = getchar())!= '\n'){
cout << c << endl;
}
}
/*一次性輸入 a b c
輸出
a
b
c
*/
2 IO類
iostream中的類型和對象都是操作char數(shù)據(jù)的。默認情況下,這些對象都是關(guān)聯(lián)到用戶控制臺的窗口的。
為了支持不同種類的IO處理操作,在istream和ostream之外,標準庫還定義了一些其他的IO類型。iostream定義了用于讀寫流的基本類型,fstream定義了讀寫命名文件的類型,sstream定義了讀寫內(nèi)存string對象的類型。

為了支持寬字符的語言,標準庫定義了一組類型和對象來操作wchar_t類型的數(shù)據(jù)。寬字符版本的類型和函數(shù)的名字以一個w開始,與其普通char版本定義在同一個頭文件中。
2.1 IO對象無拷貝
由于不能拷貝IO對象,因此我們不能將形參或返回類型設(shè)置為流類型。進行IO的函數(shù)通常以引用方式傳遞和返回流。讀寫一個IO對象會改變其狀態(tài),因此傳遞和返回的引用不能是const。
2.2 條件狀態(tài)
IO類所定義的一些函數(shù)和與機器無關(guān)的iostate標志,可以幫助我們訪問和操作流的條件狀態(tài)。

1) 條件狀態(tài)說明
上述任何一個IO對象在任意時刻都有一種狀態(tài),iostate代表狀態(tài)的枚舉,badbit,failbit,eofbit,goodbit是iostate的一個具體值。
操作good在所有錯誤位均未置位的情況下返回true。而badbit、failbit和eofbit任一被置位,檢測流的狀態(tài)條件會失敗,此時fail操作返回true。bad、fail和eof只有在相應(yīng)錯誤為被置位時才返回true。
一個流一旦發(fā)生錯誤,其上后續(xù)的IO操作都會失敗。只有當一個流處于無錯狀態(tài)時,我們才可對它讀寫數(shù)據(jù)。通常用以下語句檢查一個流在使用前的狀態(tài):
while (cin >> word)
//輸入操作成功,流保持有效,條件狀態(tài)為真
** 2) 條件狀態(tài)管理**
流對象的rdstate成員返回一個iostate值,對應(yīng)流的當前狀態(tài)。setstate操作將給定條件為置位,表示發(fā)生了對應(yīng)錯誤。
clear成員是一個重載函數(shù),提供兩個版本:clear無參數(shù)版本將復(fù)位所有錯誤標志位,帶參數(shù)版本提供一個新的iostate狀態(tài)。
2.3 管理輸出緩沖
關(guān)于緩沖區(qū)內(nèi)容本章開頭有介紹。
3 文件輸入輸出
ifstream從一個給定的文件讀取數(shù)據(jù),ofstream向一個給定文件寫入數(shù)據(jù),fstream可以讀寫給定文件。
可以用getline函數(shù)從一個ifstream讀取數(shù)據(jù),除繼承自iostream類型中行為外,fsteam還提供了一下成員來管理與流關(guān)聯(lián)的文件。

3.1 使用文件流
當需要讀寫一個文件時,可以定義一個文件流對象,并將對象與文件關(guān)聯(lián)起來。文件名可以用string類型,也可以用傳統(tǒng)C風格字符串。
1) 成員函數(shù)open和close
如果我們定義了一個空文件流對象,可以隨后用open將它與文件關(guān)聯(lián)起來。如果調(diào)用open失敗。failbit將會被置位。因此檢測open是否成功是一個好習慣。
使用close函數(shù)可關(guān)閉文件流所綁定的文件。
string text;
ifstream fstm;
fstm.open("test.txt");
if (fstm){
getline(fstm, text);
cout << text;
fstm.close();
}
else{
cout << "failed";
}
\\如果順利打開test.txt,條件為真,則從中讀取內(nèi)容,輸出并關(guān)閉文件
\\打開失敗則輸出failed
2) 自動構(gòu)造和析構(gòu)
當一個fstream離開其作用域,close會自動被調(diào)用,與之關(guān)聯(lián)的文件會自動關(guān)閉。
3.2 文件模式
在打開文件時,無論是調(diào)用 open還是以文件名作為流初始化的一部分,都需指定文件模式(file mode)。

只可以對
ofstream或fstream對象設(shè)定out模式只可以對
ifstream或fstream對象設(shè)定in模式只有打開
out模式,才可設(shè)定trunc模式只要
trunc模式未被設(shè)定,就可設(shè)定app模式。在app模式下,即使沒有顯示指定out模式,文件也總以輸出模式被打開默認情況下,以
out模式打開文件,在位指定trunc時,也將被截斷。為保留其中內(nèi)容,可以使用app模式或者in模式(文件同時讀寫)ate和binary模式可用于任何類型的文件流對象且可以與其他模式組合使用。ate模式只在打開時有效:文件打開后將定位在文件尾。以binary模式打開的流則將文件以字節(jié)序列的形式處理,而不解釋流中的字符。
默認情況下,當我們打開一個ofstream時,文件內(nèi)容會被丟棄。對于用 ofstream 打開的文件,要保存文件中存在的數(shù)據(jù),唯二方法是顯式地指定app模式或 in 模式打開:
4 string流
sstream頭文件中定義了以下三個類型來支持內(nèi)存IO。
istringstream從string中讀取數(shù)據(jù),ostringstream向string中寫入數(shù)據(jù),而stringstream即可從string讀數(shù)據(jù)也可以向string寫數(shù)據(jù)。出了從iostream中繼承的操作外,sstream中還支持以下操作:

4.1 sstream的使用
string line, word;
while (getline(cin, line)) {
istringstream stream(line);
while (stream >> word){
// do per-word processing
}
}
string中數(shù)據(jù)全部讀出后,同樣會觸發(fā)“文件結(jié)束”信號,此時while循環(huán)條件為false。
4.2 轉(zhuǎn)換或格式化
sstream類似于C語言風格中ssprintf,可以方便地構(gòu)造各種風格的string。
string format_message = "cp 3 lbj 23";
istringstream input(format_message);
ostringstream output;
int val1 = 0, val2 = 0;
string str1, str2;
input >> str1 >> val1 >> str2 >> val2;
output << val1 << " " << val2 << endl;
cout << output.str();