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