C++中的文件輸入/輸出(3)
原作:Ilia Yordanov, loobian@cpp-home.com
掌握輸入/輸出流
在這一章里,我會(huì)提及一些有用的函數(shù)。我將為你演示如何打開一個(gè)可以同時(shí)進(jìn)行讀、寫操作的文件;此外,我還將為你介紹其它打開文件的方法,以及如何判斷打開操作是否成功。因此,請(qǐng)接著往下讀!
到目前為止,我已為你所展示的只是單一的打開文件的途徑:要么為讀取而打開,要么為寫入而打開。但文件還可以以其它方式打開。迄今,你應(yīng)當(dāng)已經(jīng)認(rèn)識(shí)了下面的方法:
ifstream OpenFile(“cpp-home.txt”);
噢,這可不是唯一的方法!正如以前所提到的,以上的代碼創(chuàng)建一個(gè)類ifstream的對(duì)象,并將文件的名字傳遞給它的構(gòu)造函數(shù)。但實(shí)際上,還存在有不少的重載的構(gòu)造函數(shù),它們可以接受不止一個(gè)的參數(shù)。同時(shí),還有一個(gè)open()函數(shù)可以做同樣的事情。下面是一個(gè)以上代碼的示例,但它使用了open()函數(shù):
ifstream OpenFile;
OpenFile.open(“cpp-home.txt”);
你會(huì)問:它們之間有什么區(qū)別嗎?哦,我曾做了不少測(cè)試,結(jié)論是沒有區(qū)別!只不過如果你要?jiǎng)?chuàng)建一個(gè)文件句柄但不想立刻給它指定一個(gè)文件名,那么你可以使用open()函數(shù)過后進(jìn)行指定。順便再給出一個(gè)要使用open()函數(shù)的例子:如果你打開一個(gè)文件,然后關(guān)閉了它,又打算用同一個(gè)文件句柄打開另一個(gè)文件,這樣一來,你將需要使用open()函數(shù)。
考慮以下的代碼示例:
#include <fstream.h>
void read(ifstream &T) //pass the file stream to the function
{
//the method to read a file, that I showed you before
char ch;
while(!T.eof())
{
T.get(ch);
cout << ch;
}
cout << endl << "--------" << endl;
}
void main()
{
ifstream T("file1.txt");
read(T);
T.close();
T.open("file2.txt");
read(T);
T.close();
}
據(jù)此,只要file1.txt和file2.txt并存儲(chǔ)了文本內(nèi)容,你將看到這些內(nèi)容。
現(xiàn)在,該向你演示的是,文件名并不是你唯一可以向open()函數(shù)或者構(gòu)造函數(shù)(其實(shí)都一樣)傳遞的參數(shù)。下面是一個(gè)函數(shù)原型:
ifstream OpenFile(char *filename, int open_mode);
你應(yīng)當(dāng)知道filename表示文件的名稱(一個(gè)字符串),而新出現(xiàn)的則是open_mode(打開模式)。open_mode的值用來定義以怎樣的方式打開文件。下面是打開模式的列表:
名稱
|
描述
|
ios::in
|
打開一個(gè)可讀取文件
|
ios::out
|
打開一個(gè)可寫入文件
|
ios::app
|
你寫入的所有數(shù)據(jù)將被追加到文件的末尾,此方式使用ios::out
|
ios::ate
|
你寫入的所有數(shù)據(jù)將被追加到文件的末尾,此方式不使用ios::out
|
ios::trunk
|
刪除文件原來已存在的內(nèi)容(清空文件)
|
ios::nocreate
|
如果要打開的文件并不存在,那么以此參數(shù)調(diào)用open()函數(shù)將無法進(jìn)行。
|
ios::noreplace
|
如果要打開的文件已存在,試圖用open()函數(shù)打開時(shí)將返回一個(gè)錯(cuò)誤。
|
ios::binary
|
以二進(jìn)制的形式打開一個(gè)文件。
|
實(shí)際上,以上的值都屬于一個(gè)枚舉類型的int常量。但為了讓你的編程生涯不至于太痛苦,你可以像上表所見的那樣使用那些名稱。
下面是一個(gè)關(guān)于如何使用打開模式的例子:
#include <fstream.h>
void main()
{
ofstream SaveFile("file1.txt", ios::ate);
SaveFile << "That's new!\n";
SaveFile.close();
}
正如你在表中所看到的:使用ios::ate將會(huì)從文件的末尾開始執(zhí)行寫入。如果我沒有使用它,原來的文件內(nèi)容將會(huì)被重新寫入的內(nèi)容覆蓋掉。不過既然我已經(jīng)使用了它,那么我只會(huì)在原文件的末尾進(jìn)行添加。所以,如果file1.txt原有的內(nèi)容是這樣:
Hi! This is test from www.cpp-home.com!
那么執(zhí)行上面的代碼后,程序?qū)?huì)為它添上“That’s new!”,因此它看起來將變成這樣:
Hi! This is test from www.cpp-home.com!That’s new!
假如你打算設(shè)置不止一個(gè)的打開模式標(biāo)志,只須使用OR操作符或者是 ,像這樣:
ios::ate ios::binary
我希望現(xiàn)在你已經(jīng)明白“打開模式”是什么意思了!
現(xiàn)在,是時(shí)候向你展示一些真正有用的東西了!我敢打賭你現(xiàn)在還不知道應(yīng)當(dāng)怎樣打開一個(gè)可以同時(shí)進(jìn)行讀取和寫入操作的文件!下面就是實(shí)現(xiàn)的方法:
fstream File(“cpp-home.txt”,ios::in ios::out);
實(shí)際上,這只是一個(gè)聲明語句。我將在下面數(shù)行之后給你一個(gè)代碼示例。但此時(shí)我首先想提及一些你應(yīng)當(dāng)知道的內(nèi)容。
上面的代碼創(chuàng)建了一個(gè)名為“File”的流式文件的句柄。如你所知,它是fstream類的一個(gè)對(duì)象。當(dāng)使用fstream時(shí),你應(yīng)當(dāng)指定ios::in和ios::out作為文件的打開模式。這樣,你就可以同時(shí)對(duì)文件進(jìn)行讀、寫,而無須創(chuàng)建新的文件句柄。噢,當(dāng)然,你也可以只進(jìn)行讀或者寫的操作。那樣的話,相應(yīng)地你應(yīng)當(dāng)只使用ios::in或者只使用ios::out —— 要思考的問題是:如果你打算這么做,為什么你不分別用ifstream及ofstream來實(shí)現(xiàn)呢?
下面就先給出示例代碼:
#include <fstream.h>
void main()
{
fstream File("test.txt",ios::in ios::out);
File << "Hi!"; //將“Hi!”寫入文件
static char str[10]; //當(dāng)使用static時(shí),數(shù)組會(huì)自動(dòng)被初始化
//即是被清空為零
File.seekg(ios::beg); // 回到文件首部
// 此函數(shù)將在后面解釋
File >> str;
cout << str << endl;
File.close();
}
OK,這兒又有一些新東西,所以我將逐行進(jìn)行解釋:
fstream File(“test.txt”, ios::in ios::out); —— 此行創(chuàng)建一個(gè)fstream對(duì)象,執(zhí)行時(shí)將會(huì)以讀/寫方式打開test.txt文件。這意味著你可以同時(shí)讀取文件并寫入數(shù)據(jù)。
File << “Hi!”; —— 我打賭你已經(jīng)知道它的意思了。
static char str[10]; —— 這將創(chuàng)建一個(gè)容量為10的字符數(shù)組。我猜static對(duì)你而言或者有些陌生,如果這樣就忽略它。這只不過會(huì)在創(chuàng)建數(shù)組的同時(shí)對(duì)其進(jìn)行初始化。
File.seekg(ios::beg); —— OK,我要讓你明白它究竟會(huì)做些什么,因此我將以一些有點(diǎn)兒離題、但挺重要的內(nèi)容開始我的解釋。
還記得它么:
while(!OpenFile.eof())
{
OpenFile.get(ch);
cout << ch;
}
你是不是曾經(jīng)很想知道那背后真正執(zhí)行了什么操作?不管是或不是,我都將為你解釋。這是一個(gè)while型循環(huán),它會(huì)一直反復(fù),直至程序的操作到達(dá)文件的尾端。但這個(gè)循環(huán)如何知道是否已經(jīng)到了文件末尾?嗯,當(dāng)你讀文件的時(shí)候,會(huì)有一個(gè)類似于“內(nèi)置指針(inside-pointer)”的東西,它表明你讀取(寫入也一樣)已經(jīng)到了文件的哪個(gè)位置,就像記事本中的光標(biāo)。而每當(dāng)你調(diào)用OpenFile.get(ch)的時(shí)候,它會(huì)返回當(dāng)前位置的字符,存儲(chǔ)在ch變量中,并將這一內(nèi)置指針向前移動(dòng)一個(gè)字符。因此下次該函數(shù)再被調(diào)用時(shí),它將會(huì)返回下一個(gè)字符。而這一過程將不斷反復(fù),直到讀取到達(dá)文件尾。所以,讓我們回到那行代碼:函數(shù)seekg()將把內(nèi)置指針定位到指定的位置(依你決定)。你可以使用:
ios::beg —— 可將它移動(dòng)到文件首端
ios::end —— 可將它移動(dòng)到文件末端
或者,你可以設(shè)定向前或向后跳轉(zhuǎn)的字符數(shù)。例如,如果你要向定位到當(dāng)前位置的5個(gè)字符以前,你應(yīng)當(dāng)寫:
File.seekg(-5);
如果你想向后跳過40個(gè)字符,則應(yīng)當(dāng)寫:
File.seekg(40);
同時(shí),我必須指出,函數(shù)seekg()是被重載的,它也可以帶兩個(gè)參數(shù)。另一個(gè)版本是這樣子的:
File.seekg(-5,ios::end);
在這個(gè)例子中,你將能夠讀到文件文本的最后4個(gè)字符,因?yàn)椋?/span>
1)你先到達(dá)了末尾(ios::end)
2)你接著到達(dá)了末尾的前五個(gè)字符的位置(-5)
為什么你會(huì)讀到4個(gè)字符而不是5個(gè)?噢,只須把最后一個(gè)看成是“丟掉了”,因?yàn)槲募钅┒说?#8220;東西”既不是字符也不是空白符,那只是一個(gè)位置(譯注:或許ios::end所“指”的根本已經(jīng)超出了文件本身的范圍,確切的說它是指向文件最后一個(gè)字符的下一個(gè)位置,有點(diǎn)類似STL中的各個(gè)容器的end迭代點(diǎn)是指向最后一個(gè)元素的下一位置。這樣設(shè)計(jì)可能是便于在循環(huán)中實(shí)現(xiàn)遍歷)。
你現(xiàn)在可能想知道為什么我要使用到這個(gè)函數(shù)。呃,當(dāng)我把“Hi”寫進(jìn)文件之后,內(nèi)置指針將被設(shè)為指向其后面……也就是文件的末尾。因此我必須將內(nèi)置指針設(shè)回文件起始處。這就是這個(gè)函數(shù)在此處的確切用途。
File >> str; —— 這也是新鮮的玩意兒!噢,我確信這行代碼讓你想起了cin >> .實(shí)際上,它們之間有著相當(dāng)?shù)年P(guān)聯(lián)。此行會(huì)從文件中讀取一個(gè)單詞,然后將它存入指定的數(shù)組變量中。
例如,如果文件中有這樣的文本片斷:
Hi! Do you know me?
使用File >> str,則只會(huì)將“Hi!”輸出到str數(shù)組中。你應(yīng)當(dāng)已經(jīng)注意到了,它實(shí)際上是將空格作為單詞的分隔符進(jìn)行讀取的。
由于我存入文件中的只是單獨(dú)一個(gè)“Hi!”,我不需要寫一個(gè)while循環(huán),那會(huì)花費(fèi)更多的時(shí)間來寫代碼。這就是我使用此方法的原因。順便說一下,到目前為止,我所使用的讀取文件的
========================
文件拷貝:
void copyfile(const char* src,const char* des)


{
fstream infile(src,ios::binary);
fstream outfile(des,ios::binary); ;
outfile<< infile.rd_buf();
}