C++ 标准输入输出流
本來想好好總結(jié)下C++輸入輸出流的
內(nèi)容,看到GitHub上面xuelangZF,總結(jié)的已經(jīng)非常好了,在此轉(zhuǎn)載過來,原文網(wǎng)址:https://github.com/xuelangZF/CS_Offer/blob/master/C%2B%2B/InputOutput.md
C++ 輸入輸出流
輸入和輸出并不是C++語言中的正式組成成分。C和C++本身都沒有為輸入和輸出提供專門的語句結(jié)構(gòu)。輸入輸出不是由C++本身定義的,而是在編譯系統(tǒng)提供的I/O庫中定義的。
C++ 的程序把輸入和輸出看做字節(jié)流。輸入時,程序從輸入流中抽取字節(jié);輸出時,程序?qū)⒆止?jié)插入輸出流中,流充當(dāng)了程序和流源或流目標(biāo)之間的橋梁。
C++輸入輸出中有兩個基類,分別為ios_base和ios,而基于這兩個基類實現(xiàn)了輸入流istream和輸出流ostream,然后基于這兩個流實現(xiàn)了針對三種不同應(yīng)用的類(或?qū)ο?#xff09;:
如下圖:
注意 I/O 對象不能進行拷貝或者賦值操作!
I/O 操作一個與生俱來的問題是可能發(fā)生錯誤,I/O類定義了一些函數(shù)和標(biāo)志,用來訪問和操縱流的條件狀態(tài)。一共有4個流狀態(tài):
- strm::badbit:流已經(jīng)崩潰
- strm::failbit:有一個 I/O 操作失敗
- strm::eofbit:到達文件結(jié)尾
- strm::goodbit:用來指出流未處于錯誤狀態(tài)
一旦一個流發(fā)生錯誤,其上后續(xù)的 IO 操作都會失敗,只有當(dāng)一個流處于無錯狀態(tài)時,才可以從它讀取數(shù)據(jù),向它寫入數(shù)據(jù)。確保一個流對象的狀態(tài)的最簡單的方法是將它當(dāng)作一個條件來使用。
while(cin >> word){// 讀操作成功 }標(biāo)準(zhǔn)輸入輸出
使用?cin?讀取數(shù)據(jù),操作符讀取指定類型的數(shù)據(jù),直到與目標(biāo)類型不匹配的第一個字符為止,期間會跳過空格,制表符和tab。
注意,不能用cin語句把空格字符和回車換行符作為字符輸入給字符變量,它們將被跳過。如果想將空格字符或回車換行符(或任何其他鍵盤上的字符)輸入給字符變量,可以使用getchar函數(shù)。
使用?cout?輸出數(shù)據(jù)時,可以使用控制符(控制符在頭文件 iomanip 中),來進行格式化輸出。例如要求以十六進制或八進制形式輸出一個整數(shù),或者對輸出的小數(shù)只保留兩位小數(shù)等。常用的控制符如下:
| dec | 設(shè)置數(shù)值的基數(shù)為10 |
| hex | 設(shè)置數(shù)值的基數(shù)為16 |
| oct | 設(shè)置數(shù)值的基數(shù)為8 |
| setprecision(n) | 設(shè)置浮點數(shù)的精度為n位。在以一般十進制小數(shù)形式輸出時,n代表有效數(shù)字。在以fixed(固定小數(shù)位數(shù))形式和 scientific(指數(shù))形式輸出時,n為小數(shù)位數(shù) |
| setw(n) | 設(shè)置字段寬度為n位 |
| setiosflags( ios::fixed) | 設(shè)置浮點數(shù)以固定的小數(shù)位數(shù)顯示(ios::left 左對齊,ios::right 數(shù)據(jù)右對齊) |
文件輸入輸出
文件流是以外存文件為輸入輸出對象的數(shù)據(jù)流。輸出文件流是從內(nèi)存流向外存文件的數(shù)據(jù),輸入文件流是從外存文件流向內(nèi)存的數(shù)據(jù)。每一個文件流都有一個內(nèi)存緩沖區(qū)與之對應(yīng)。
頭文件 fstream 定義了3個用于文件操作的文件類:
- ifstream類,它是從istream類派生的,用來支持從磁盤文件的輸入。
- ofstream類,它是從ostream類派生的,用來支持向磁盤文件的輸出。
- fstream類,它是從iostream類派生的,用來支持對磁盤文件的輸入輸出。
要以磁盤文件為對象進行輸入輸出,必須定義一個文件流類的對象,并將對象與文件關(guān)聯(lián)起來。其實在用標(biāo)準(zhǔn)設(shè)備為對象的輸入輸出中,也是要定義流對象的,如cin、cout就是流對象,C++是通過流對象進行輸入輸出的。由于cin、cout已在iostream.h中事先定義,所以用戶不需自己定義。在用磁盤文件時,由于情況各異,無法事先統(tǒng)一定義,必須由用戶自己定義。
每一個文件流對象都定義了一個成員函數(shù) open 來定位給定的文件,并視情況打開為讀或?qū)懩J健H绻趧?chuàng)建文件流對象時,提供了文件名,則open會被自動調(diào)用。沒有提供文件名,將會創(chuàng)建空文件流對象,可以隨后調(diào)用 open 將它與文件關(guān)聯(lián)起來。
ifstream in(ifile); // 構(gòu)造一個 ifstream 對象并打開給定文件; ofstream out; // 輸出文件流,未關(guān)聯(lián)任何文件 out.open(ofile); // 打開指定文件調(diào)用 open 可能會失敗(相應(yīng)文件不存在或者對一個打開的文件流再次調(diào)用open),因此需要檢查 open 是否成功:
if(out)一旦一個文件流已經(jīng)打開,就會保持與對應(yīng)文件的關(guān)聯(lián)。為了將文件流關(guān)聯(lián)到另一個文件,必須首先用 close 函數(shù)關(guān)閉已經(jīng)關(guān)聯(lián)的文件。
out.close(); out.open(ofile_2);注意,當(dāng)一個 ifstream 對象離開其作用域時,與之關(guān)聯(lián)的文件會自動關(guān)閉。
文件模式
每個流都有一個關(guān)聯(lián)的 文件模式(file mode),用來指出如何使用文件。
| in | 以讀方式打開 |
| out | 以寫方式打開 |
| app | 每次寫操作均定位到文件末尾,只要沒有trunc,就可以設(shè)定app |
| ate | 打開文件后立即定位到文件末尾,適用于任何類型的文件流對象 |
| trunc | 截斷文件(out被設(shè)定才可設(shè)定trunc,out默認(rèn)是trunc的) |
| binary | 以二進制方式進行 IO,適用于任何類型的文件流對象 |
每個文件流類型都定義了一個默認(rèn)的文件模式,ifstream 關(guān)聯(lián)的文件默認(rèn) in 打開,ofstream 關(guān)聯(lián)的文件默認(rèn) out 模式打開,fstream 關(guān)聯(lián)的文件默認(rèn) in 和 out 打開。下面是一些打開時選定模式的例子:
ofstream outfile("filename", ofstream::out); // 隱含截斷文件 ofstream outfile("filename", ofstream::app); // 追加模式字符串輸入輸出
sstream 頭文件定義了三個類型來支持內(nèi)存 IO,這些類型可以向 string 寫入數(shù)據(jù),從 string 讀取數(shù)據(jù),就像 string 是一個 IO 流一樣。
輸出時數(shù)據(jù)不是流向外存文件,而是流向內(nèi)存中的一個存儲空間。輸入時從內(nèi)存中的存儲空間讀取數(shù)據(jù)。在嚴(yán)格的意義上說,這不屬于輸入輸出,稱為讀寫比較合適。 因為輸入輸出一般指的是在計算機內(nèi)存與計算機外的文件(外部設(shè)備也視為文件)之間 的數(shù)據(jù)傳送。
sstream 定義來的類型增加了一些成員來管理與流相關(guān)聯(lián)的 string,可以對 stringstream 對象調(diào)用這些操作,但不能對其它 IO 調(diào)用。
- stringstream strm: 未綁定的 stringstream 對象;
- stringstream strm(s):一個 stringstream 對象,保存了 string s 的一個拷貝;
- strm.str():返回 strm 所保存的 string 的拷貝;
- strm.str(s):將 string s拷貝到 strm 中,返回 void.
對字符串流的幾點說明:
一個簡單的例子,反轉(zhuǎn) vector 中的數(shù)字:
vector<int> nums{123,456,789}; vector<int> reversed_nums; for(auto n : nums){ostringstream digit;digit << n;string str_n = digit.str();reverse(str_n.begin(), str_n.end());int reversed_n;istringstream rev_n(str_n);rev_n >> reversed_n;reversed_nums.push_back(reversed_n); } // reversed_nums: {321, 654, 987}我們知道在要求使用基類對象的地方,可以使用繼承類型的對象取代,所以在接受一個 iostream 類型引用或者指針參數(shù)的函數(shù),可以用一個對應(yīng)的 fstream(或 sstream)類型來調(diào)用。
相關(guān)函數(shù)
istream, ostream 類提供了許多函數(shù),常用的有 get, getline 等。
std::istream::get?從輸入流中讀取一個字符,賦給字符變量ch,常用的原型為:
istream& get (char& c);std::istream::getline?從輸入流中讀取字符,直到遇到終止符號,默認(rèn)換行符為終止符號,讀到換行符后,丟棄換行符(C++ 還提供一個功能類似的全局函數(shù) std::getline)。常用的原型如下:
istream& getline (char* s, streamsize n ); istream& getline (char* s, streamsize n, char delim );用getline函數(shù)從輸入流讀字符時,遇到終止標(biāo)志字符時結(jié)束,指針移到該終止標(biāo)志字符之后,下一個getline函數(shù)將從該終止標(biāo)志的下一個字符開始接著讀入。如果用cin.get函數(shù)從輸入流讀字符時,遇終止標(biāo)志字符時停止讀取,指針不向后移動,仍然停留在原位置,下一次讀取時仍從該終止標(biāo)志字符開始,這是getline函數(shù)和get函數(shù)不同之處。簡單來說,getline將丟棄換行符,而get()將換行符保留在輸入序列里,千萬要注意 get 之后的換行符(?>> 操作符也不會丟棄換行符,同樣需要注意)。
輸入輸出緩沖區(qū)
有時候程序會出現(xiàn)奇怪的輸出,或者輸入中讀取的數(shù)據(jù)和想象不一致,這通常是由于存在輸出輸入緩沖區(qū)導(dǎo)致的。
在定義流對象時,系統(tǒng)會在內(nèi)存中開辟一段緩沖區(qū),用來暫存輸入輸出流的數(shù)據(jù)。緩沖區(qū)是用作中介的內(nèi)存塊,它是將信息從設(shè)備傳輸?shù)匠绦蚧蛘邚某绦騻鬏數(shù)皆O(shè)備的臨時存儲工具,用以匹配程序和設(shè)備之間速率的差距。
輸出緩沖區(qū)
通常會把輸出流中內(nèi)容順序存放在輸出緩沖區(qū)中,直到輸出緩沖區(qū)滿或遇到cout語句中的endl(或'\n',flush)為止,此時將緩沖區(qū)中已有的數(shù)據(jù)一起輸出,并清空緩沖區(qū)。看下面的例子:
#include <stdio.h> #include <sys/types.h> #include <unistd.h>int main(void) {int i;for(i=0; i<2; i++){fork();printf("-");}// 輸出 8 個 -return 0; }輸入緩沖區(qū)
cin.ignore(a, ch)方法是從輸入流(cin)中提取字符,提取的字符被忽略(ignore)。如果遇到 a 個字母或者遇到 ch,則執(zhí)行終止;否則,它繼續(xù)等待。它的一個常用功能就是用來清除以回車結(jié)束的輸入緩沖區(qū)的內(nèi)容,消除上一次輸入對下一次輸入的影響。下面是一個簡單的示例:
char first, second;cout << "Please, enter a word: "; first = cin.get(); cin.clear(); cin.ignore(3);cout << "Please, enter another word: "; second = cin.get();cout << "The first word began by " << first << '\n'; cout << "The second word began by " << second << '\n';如果輸入發(fā)生錯誤,那么流狀態(tài)會被標(biāo)記,必須用?cin.clear()?清除錯誤狀態(tài),以使程序能正確適當(dāng)?shù)乩^續(xù)運行。
不建議使用?std::cin.sync()?丟棄緩沖區(qū)內(nèi)容,因為有的平臺并不支持(OS X就不支持)。
陷阱
OJ 輸入提前 break!!!有時候會犯這類錯誤,且不容易察覺。
while(cin >> p >> n){set<int> basket;int ans = -1;for(int i=0;i<n;i++){int xi;cin >> xi;if(basket.find(xi%p)!=basket.end()){ans = i+1;break; // 后面的數(shù)據(jù)并沒有讀,仍然在緩沖區(qū)中!!!}else{basket.insert(xi%p);}}cout << ans << endl; }讀取操作時,>> 操作符?會跳過空白制表符,但是 getline 不會跳過。也就是說 getline 可能會讀取 >> 操作后剩余下的換行符。假設(shè)一個輸入流數(shù)據(jù)如下:
10 2
name Jack
name John
如果用下面的程序讀取數(shù)據(jù):
cin >> N >> M; for(int i=0; i<2; i++){cin.getline(names[i]); }那么讀到的names數(shù)組前兩項將會是 "", "name Jack"。因為 >> 讀完 M 后還剩下一個換行符,將被 getline 讀取到。解決辦法就是在 第一句后面加上一句,吃掉換行符。(后面就不用吃掉換行符了,因為 getline 會丟棄換行符)
cin >> N >> M; cin.get(); ...參考
C++ Primer 文件輸入輸出
cplusplus: Clear Input Stream
cplusplus: Flush output stream buffer?How do I flush the cin buffer?
The difference between cin.ignore and cin.sync
C++輸入輸出詳解:C++標(biāo)準(zhǔn)輸入輸出流、文件流、字符串流
總結(jié)
以上是生活随笔為你收集整理的C++ 标准输入输出流的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: C++ 如何将一个文件夹中的所有文件(.
- 下一篇: C++对自定义结构体变量排序