引言
c++不直接處理輸入和輸出,而是通過標準庫中的型別處理IO。IO的裝置可以是檔案、控制檯、string。c++主要定義了三種IO型別,分別被包含在iostream
、fstream
、sstream
標頭檔案中。
為了支援使用寬字元的語言,標準庫定義了一組型別和物件操縱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!"可能被立即列印出來,也可能被作業系統儲存在緩衝區,隨後列印。
以下原因可以重新整理緩衝(即真正將資料輸出到目標裝置或檔案中):
- 程式正常結束,自動重新整理。
- 緩衝區滿時,自動重新整理。
- 可以使用操作符
endl
、flush
、ends
手動重新整理緩衝區(只作用一次輸出)。
cout << "1" << endl; //字串後新增換行,然後重新整理緩衝區
cout << "2" << flush; //僅重新整理緩衝區
cout << "3" << ends; //字串後新增一個空字元,然後重新整理緩衝區。
- 通過操作符unitbuf設定自動重新整理。不同於
endl
、flush
、ends
只作用於一次輸出,設定了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 第五版》的個人總結與疑難解釋,主要用於個人日後複習。
如果想要深入瞭解更多,請支援正版。