一、ASCII 輸出
為了使用下面的方法, 你必須包含頭文件<fstream.h>(譯者注:在標(biāo)準(zhǔn)C++中,已經(jīng)使用<fstream>取代<fstream.h>,所有的C++標(biāo)準(zhǔn)頭文件都是無后綴的。)。這是 <iostream.h>的一個(gè)擴(kuò)展集, 提供有緩沖的文件輸入輸出操作. 事實(shí)上, <iostream.h> 已經(jīng)被<fstream.h>包含了, 所以你不必包含所有這兩個(gè)文件, 如果你想顯式包含他們,那隨便你。我們從文件操作類的設(shè)計(jì)開始, 我會講解如何進(jìn)行ASCII I/O操作。 如果你猜是"fstream," 恭喜你答對了! 但這篇文章介紹的方法,我們分別使用"ifstream"?和 "ofstream" 來作輸入輸出。
如果你用過標(biāo)準(zhǔn)控制臺流"cin"?和 "cout," 那現(xiàn)在的事情對你來說很簡單。 我們現(xiàn)在開始講輸出部分,首先聲明一個(gè)類對象。ofstream fout;
這就可以了,不過你要打開一個(gè)文件的話, 必須像這樣調(diào)用ofstream::open()。
你也可以把文件名作為構(gòu)造參數(shù)來打開一個(gè)文件.
ofstream fout("output.txt"); |
這是我們使用的方法, 因?yàn)檫@樣創(chuàng)建和打開一個(gè)文件看起來更簡單. 順便說一句, 如果你要打開的文件不存在,它會為你創(chuàng)建一個(gè), 所以不用擔(dān)心文件創(chuàng)建的問題. 現(xiàn)在就輸出到文件,看起來和"cout"的操作很像。 對不了解控制臺輸出"cout"的人, 這里有個(gè)例子。
int num = 150; char name[] = "John Doe"; fout << "Here is a number: " << num << "\n"; fout << "Now here is a string: " << name << "\n"; |
現(xiàn)在保存文件,你必須關(guān)閉文件,或者回寫文件緩沖. 文件關(guān)閉之后就不能再操作了, 所以只有在你不再操作這個(gè)文件的時(shí)候才調(diào)用它,它會自動保存文件。 回寫緩沖區(qū)會在保持文件打開的情況下保存文件, 所以只要有必要就使用它。 回寫看起來像另一次輸出, 然后調(diào)用方法關(guān)閉。像這樣:
fout << flush; fout.close(); |
現(xiàn)在你用文本編輯器打開文件,內(nèi)容看起來是這樣:
Here is a number: 150 Now here is a string: John Doe
很簡單吧! 現(xiàn)在繼續(xù)文件輸入, 需要一點(diǎn)技巧, 所以先確認(rèn)你已經(jīng)明白了流操作,對 "<<" 和">>" 比較熟悉了, 因?yàn)槟憬酉聛磉€要用到他們。繼續(xù)…
二、ASCII 輸入 輸入和"cin" 流很像. 和剛剛討論的輸出流很像, 但你要考慮幾件事情。在我們開始復(fù)雜的內(nèi)容之前, 先看一個(gè)文本:
12 GameDev 15.45 L This is really awesome!
為了打開這個(gè)文件,你必須創(chuàng)建一個(gè)in-stream對象,?像這樣。
ifstream fin("input.txt"); |
現(xiàn)在讀入前四行. 你還記得怎么用"<<" 操作符往流里插入變量和符號吧?好,?在 "<<" (插入)?操作符之后,是">>" (提取) 操作符. 使用方法是一樣的. 看這個(gè)代碼片段.
int number; float real; char letter, word[8]; fin >> number; fin >> word; fin >> real; fin >> letter; |
也可以把這四行讀取文件的代碼寫為更簡單的一行。
fin >> number >> word >> real >> letter; |
它是如何運(yùn)作的呢? 文件的每個(gè)空白之后, ">>" 操作符會停止讀取內(nèi)容, 直到遇到另一個(gè)>>操作符. 因?yàn)槲覀冏x取的每一行都被換行符分割開(是空白字符), ">>" 操作符只把這一行的內(nèi)容讀入變量。這就是這個(gè)代碼也能正常工作的原因。但是,可別忘了文件的最后一行。
This is really awesome!
如果你想把整行讀入一個(gè)char數(shù)組, 我們沒辦法用">>"?操作符,因?yàn)槊總€(gè)單詞之間的空格(空白字符)會中止文件的讀取。為了驗(yàn)證:
char sentence[101]; fin >> sentence; |
我們想包含整個(gè)句子, "This is really awesome!" 但是因?yàn)榭瞻? 現(xiàn)在它只包含了"This". 很明顯, 肯定有讀取整行的方法, 它就是getline()。這就是我們要做的。
fin.getline(sentence, 100); |
這是函數(shù)參數(shù). 第一個(gè)參數(shù)顯然是用來接受的char數(shù)組. 第二個(gè)參數(shù)是在遇到換行符之前,數(shù)組允許接受的最大元素?cái)?shù)量. 現(xiàn)在我們得到了想要的結(jié)果:“This is really awesome!”。
你應(yīng)該已經(jīng)知道如何讀取和寫入ASCII文件了。但我們還不能罷休,因?yàn)槎M(jìn)制文件還在等著我們。
三、二進(jìn)制 輸入輸出
二進(jìn)制文件會復(fù)雜一點(diǎn), 但還是很簡單的。 首先你要注意我們不再使用插入和提取操作符(譯者注:<< 和 >> 操作符). 你可以這么做,但它不會用二進(jìn)制方式讀寫。你必須使用read() 和write() 方法讀取和寫入二進(jìn)制文件. 創(chuàng)建一個(gè)二進(jìn)制文件, 看下一行。
ofstream fout("file.dat", ios::binary); |
這會以二進(jìn)制方式打開文件, 而不是默認(rèn)的ASCII模式。首先從寫入文件開始。函數(shù)write() 有兩個(gè)參數(shù)。 第一個(gè)是指向?qū)ο蟮腸har類型的指針, 第二個(gè)是對象的大?。ㄗg者注:字節(jié)數(shù))。 為了說明,看例子。
int number = 30; fout.write((char *)(&number), sizeof(number)); |
第一個(gè)參數(shù)寫做"(char *)(&number)". 這是把一個(gè)整型變量轉(zhuǎn)為char *指針。如果你不理解,可以立刻翻閱C++的書籍,如果有必要的話。第二個(gè)參數(shù)寫作"sizeof(number)". sizeof() 返回對象大小的字節(jié)數(shù). 就是這樣!
二進(jìn)制文件最好的地方是可以在一行把一個(gè)結(jié)構(gòu)寫入文件。 如果說,你的結(jié)構(gòu)有12個(gè)不同的成員。 用ASCII?文件,你不得不每次一條的寫入所有成員。 但二進(jìn)制文件替你做好了。 看這個(gè)。
struct OBJECT { int number; char letter; } obj; obj.number = 15; obj.letter = ‘M’; fout.write((char *)(&obj), sizeof(obj)); |
這樣就寫入了整個(gè)結(jié)構(gòu)! 接下來是輸入. 輸入也很簡單,因?yàn)閞ead()?函數(shù)的參數(shù)和 write()是完全一樣的, 使用方法也相同。
ifstream fin("file.dat", ios::binary); fin.read((char *)(&obj), sizeof(obj)); |
我不多解釋用法, 因?yàn)樗蛍rite()是完全相同的。二進(jìn)制文件比ASCII文件簡單, 但有個(gè)缺點(diǎn)是無法用文本編輯器編輯。 接著, 我解釋一下ifstream 和ofstream 對象的其他一些方法作為結(jié)束.
四、更多方法
我已經(jīng)解釋了ASCII文件和二進(jìn)制文件, 這里是一些沒有提及的底層方法。
檢查文件
你已經(jīng)學(xué)會了open() 和close() 方法, 不過這里還有其它你可能用到的方法。
方法good() 返回一個(gè)布爾值,表示文件打開是否正確。
類似的,bad() 返回一個(gè)布爾值表示文件打開是否錯誤。 如果出錯,就不要繼續(xù)進(jìn)一步的操作了。
最后一個(gè)檢查的方法是fail(), 和bad()有點(diǎn)相似, 但沒那么嚴(yán)重。
讀文件
方法get() 每次返回一個(gè)字符。
方法ignore(int,char) 跳過一定數(shù)量的某個(gè)字符, 但你必須傳給它兩個(gè)參數(shù)。第一個(gè)是需要跳過的字符數(shù)。 第二個(gè)是一個(gè)字符, 當(dāng)遇到的時(shí)候就會停止。 例子,
會跳過100個(gè)字符,或者不足100的時(shí)候,跳過所有之前的字符,包括 ‘\n’。
方法peek() 返回文件中的下一個(gè)字符, 但并不實(shí)際讀取它。所以如果你用peek() 查看下一個(gè)字符, 用get() 在peek()之后讀取,會得到同一個(gè)字符, 然后移動文件計(jì)數(shù)器。
方法putback(char) 輸入字符, 一次一個(gè), 到流中。我沒有見到過它的使用,但這個(gè)函數(shù)確實(shí)存在。
寫文件
只有一個(gè)你可能會關(guān)注的方法.?那就是 put(char), 它每次向輸出流中寫入一個(gè)字符。
打開文件
當(dāng)我們用這樣的語法打開二進(jìn)制文件:
ofstream fout("file.dat", ios::binary); |
"ios::binary"是你提供的打開選項(xiàng)的額外標(biāo)志. 默認(rèn)的, 文件以ASCII方式打開, 不存在則創(chuàng)建, 存在就覆蓋. 這里有些額外的標(biāo)志用來改變選項(xiàng)。
ios::app 添加到文件尾
ios::ate 把文件標(biāo)志放在末尾而非起始。
ios::trunc 默認(rèn). 截?cái)嗖⒏矊懳募?
ios::nocreate 文件不存在也不創(chuàng)建。
ios::noreplace 文件存在則失敗。
文件狀態(tài)
我用過的唯一一個(gè)狀態(tài)函數(shù)是eof(), 它返回是否標(biāo)志已經(jīng)到了文件末尾。 我主要用在循環(huán)中。 例如, 這個(gè)代碼斷統(tǒng)計(jì)小寫‘e’ 在文件中出現(xiàn)的次數(shù)。
ifstream fin("file.txt"); char ch; int counter; while (!fin.eof()) { ch = fin.get(); if (ch == ‘e’) counter++; } fin.close(); |
我從未用過這里沒有提到的其他方法。 還有很多方法,但是他們很少被使用。參考C++書籍或者文件流的幫助文檔來了解其他的方法。