深化了解C++中的文件操作。本站提示廣大學習愛好者:(深化了解C++中的文件操作)文章只能為提供參考,不一定能成為您想要的結果。以下是深化了解C++中的文件操作正文
前言
我們在編寫順序的時分,最密不可分的就是對文件停止相應的操作,我們可以從文件中讀取數據,可以將數據保管到文件,可以……
總而言之,言而總之,一言以蔽之,對文件的操作是十分重要的,上面我們就來引見一下C++中是如何對文件停止操作的。C++
經過以下幾個類支持文件的輸出輸入:
ofstream: 寫操作(輸入)的文件類 (由ostream引申而來)
ifstream: 讀操作(輸出)的文件類(由istream引申而來)
fstream: 可同時讀寫操作的文件類 (由iostream引申而來)
翻開文件(Open a file)
對這些類的一個對象所做的第一個操作通常就是將它和一個真正的文件聯絡起來,也就是說翻開一個文件。被翻開的文件在順序中由一個流對象(stream object)來表示 (這些類的一個實例) ,而對這個流對象所做的任何輸出輸入操作實踐就是對該文件所做的操作。
要經過一個流對象翻開一個文件,我們運用它的成員函數open():void open (const char * filename, openmode mode);
這裡filename 是一個字符串,代表要翻開的文件名,mode 是以下標志符的一個組合: ios::in
為輸出(讀)而翻開文件
ios::out
為輸入(寫)而翻開文件
ios::ate
初始地位:文件尾
ios::app
一切輸入附加在文件末尾
ios::trunc
假如文件已存在則先刪除該文件
ios::binary
二進制方式
這些標識符可以被組合運用,兩頭以”或”操作符(|)距離。例如,假如我們想要以二進制方式翻開文件”example.bin” 來寫入一些數據,我們可以經過以下方式調用成員函數open()來完成:
ofstream file; file.open ("example.bin", ios::out | ios::app | ios::binary);
ofstream, ifstream 和 fstream一切這些類的成員函數open 都包括了一個默許翻開文件的方式,這三個類的默許方式各不相反: 類 參數的默許方式
ofstream ios::out | ios::trunc
ifstream ios::in
fstream ios::in | ios::out
只要當函數被調用時沒有聲明方式參數的狀況下,默許值才會被采用。假如函數被調用時聲明了任何參數,默許值將被完全改寫,而不會與調用參數組合。
由 於對類ofstream, ifstream 和 fstream 的對象所停止的第一個操作通常都是翻開文件,這些類都有一個結構函數可以直接調用open 函數,並擁有異樣的參數。這樣,我們就可以經過以下方式停止與下面異樣的定義對象和翻開文件的操作:
ofstream file ("example.bin", ios::out | ios::app | ios::binary);
兩種翻開文件的方式都是正確的。
你可以經過調用成員函數is_open()來反省一個文件能否曾經被順利的翻開了:bool is_open();
它前往一個布爾(bool)值,為真(true)代表文件曾經被順利翻開,假( false )則相反。
封閉文件(Closing a file)
當文件讀寫操作完成之後,我們必需將文件封閉以使文件重新變為可訪問的。封閉文件需求調用成員函數close(),它擔任將緩存中的數據排放出來並封閉文件。它的格式很復雜:
void close ();
這個函數一旦被調用,原先的流對象(stream object)就可以被用來翻開其它的文件了,這個文件也就可以重新被其它的進程(process)一切訪問了。
為避免流對象被銷毀時還聯絡著翻開的文件,析構函數(destructor)將會自動調用封閉函數close。
文本文件(Text mode files)
類ofstream, ifstream 和fstream 是辨別從ostream, istream 和iostream 中引申而來的。這就是為什麼 fstream 的對象可以運用其父類的成員來訪問數據。
普通來說,我們將運用這些類與同控制台(console)交互異樣的成員函數(cin 和 cout)來停止輸出輸入。如上面的例題所示,我們運用重載的拔出操作符
// writing on a text file #include <fstream> using namespace std; int main() { ofstream examplefile("example.txt"); if (examplefile.is_open()) { examplefile << "This is a line.\n"; examplefile << "This is another line.\n"; examplefile.close(); } return 0; }
從文件中讀入數據也可以用與 cin的運用異樣的辦法:
// reading a text file #include <iostream> #include <fstream> #include <cstdlib> using namespace std; int main () { char buffer[256]; ifstream examplefile("example.txt"); if (! examplefile.is_open()) { cout << "Error opening file"; exit (1); } while (!examplefile.eof()) { examplefile.getline(buffer,100); cout<<buffer<< endl; } return 0; } //This is a line. //This is another line.
下面的例子讀入一個文本文件的內容,然後將它打印到屏幕上。留意我們運用了一個新的成員函數叫做eof ,它是ifstream 從類 ios 中承繼過去的,當抵達文件末尾時前往true 。
形態標志符的驗證(Verification of state flags)
除了eof()
以外,還有一些驗證流的形態的成員函數(一切都前往bool型前往值):
bad()
假如在讀寫進程中出錯,前往 true 。例如:當我們要對一個不是翻開為寫形態的文件停止寫入時,或許我們要寫入的設備沒有剩余空間的時分。
fail()
除了與bad() 異樣的狀況下會前往 true 以外,加上格式錯誤時也前往true ,例如當想要讀入一個整數,而取得了一個字母的時分。
eof()
假如讀文件抵達文件末尾,前往true。
good()
這是最通用的:假如調用以上任何一個函數前往true 的話,此函數前往 false 。
要想重置以上成員函數所反省的形態標志,你可以運用成員函數clear(),沒有參數。
取得和設置流指針(get and put stream pointers)
一切輸出/輸入流對象(i/o streams objects)都有至多一個流指針:
ifstream, 相似istream, 有一個被稱為get pointer的指針,指向下一個將被讀取的元素。
ofstream, 相似 ostream, 有一個指針 put pointer ,指向寫入下一個元素的地位。
fstream, 相似 iostream, 同時承繼了get 和 put
我們可以經過運用以下成員函數來讀出或配置這些指向流中讀寫地位的流指針:
tellg()
和 tellp()
這兩個成員函數不必傳入參數,前往pos_type 類型的值(依據ANSI-C++ 規范) ,就是一個整數,代表以後get 流指針的地位 (用tellg) 或 put 流指針的地位(用tellp).
seekg()
和seekp()
這對函數辨別用來改動流指針get 和put的地位。兩個函數都被重載為兩種不同的原型:
seekg ( pos_type position ); seekp ( pos_type position );
運用這個原型,流指針被改動為指向從文件開端計算的一個相對地位。要求傳入的參數類型與函數 tellg 和tellp 的前往值類型相反。
seekg ( off_type offset, seekdir direction ); seekp ( off_type offset, seekdir direction );
運用這個原型可以指定由參數direction決議的一個詳細的指針開端計算的一個位移(offset)。它可以是:
ios::beg
從流開端地位計算的位移
ios::cur
從流指針以後地位開端計算的位移
ios::end
從流末尾處開端計算的位移
流指針 get 和 put 的值對文本文件(text file)和二進制文件(binary file)的計算辦法都是不同的,由於文本形式的文件中某些特殊字符能夠被修正。由於這個緣由,建議對以文本文件形式翻開的文件總是運用seekg 和 seekp的第一種原型,而且不要對tellg 或 tellp 的前往值停止修正。對二進制文件,你可以恣意運用這些函數,應該不會有任何不測的行為發生。
以下例子運用這些函數來取得一個二進制文件的大小:
// obtaining file size #include <iostream> #include <fstream> using namespace std; int main () { const char * filename = "example.txt"; long l,m; ifstream file(filename, ios::in|ios::binary); l = file.tellg(); file.seekg(0, ios::end); m = file.tellg(); file.close(); cout <<"size of "<< filename; cout <<" is "<< (m-l)<<" bytes.\n"; return 0; } //size of example.txt is 40 bytes.
二進制文件(Binary files)
在二進制文件中,運用>,以及函數(如getline)來操作符輸出和輸入數據,沒有什麼實踐意義,雖然它們是契合語法的。
文 件流包括兩個為順序讀寫數據特殊設計的成員函數:write 和 read。第一個函數 (write) 是ostream 的一個成員函數,都是被ofstream所承繼。而read 是istream 的一個成員函數,被ifstream 所承繼。類 fstream 的對象同時擁有這兩個函數。它們的原型是:
write ( char * buffer, streamsize size ); read ( char * buffer, streamsize size );
這裡 buffer 是一塊內存的地址,用來存儲或讀出數據。參數size 是一個整數值,表示要從緩存(buffer)中讀出或寫入的字符數。
// reading binary file #include <iostream> #include <fstream> using namespace std; int main () { const char * filename = "example.txt"; char * buffer; long size; ifstream file(filename, ios::in|ios::binary|ios::ate); size = file.tellg(); file.seekg(0, ios::beg); buffer = new char [size]; file.read(buffer, size); file.close(); cout <<"the complete file is in a buffer"; delete[] buffer; return 0; } //The complete file is in a buffer
緩存和同步(Buffers and Synchronization)
當我們對文件流停止操作的時分,它們與一個streambuf 類型的緩存(buffer)聯絡在一同。這個緩存(buffer)實踐是一塊內存空間,作為流(stream)和物理文件的媒介。例如,關於一個輸入流, 每次成員函數put (寫一個單個字符)被調用,這個字符不是直接被寫入該輸入流所對應的物理文件中的,而是首先被拔出到該流的緩存(buffer)中。
當緩存被排放出來(flush)時,它外面的一切數據或許被寫入物理媒質中(假如是一個輸入流的話),或許復雜的被抹掉(假如是一個輸出流的話)。這個進程稱為同步(synchronization),它會在以下任一狀況下發作:
當文件被封閉時: 在文件被封閉之前,一切還沒有被完全寫出或讀取的緩存都將被同步。
當緩存buffer 滿時:緩存Buffers 有一定的空間限制。當緩存滿時,它會被自動同步。
控制符明白指明:當遇到流中某些特定的控制符時,同步會發作。這些控制符包括:flush 和endl。
明白調用函數sync()
: 調用成員函數sync() (無參數)可以引發立刻同步。這個函數前往一個int 值,等於-1 表示流沒有聯絡的緩存或操作失敗
在C++中,有一個stream這個類,一切的I/O都以這個“流”類為根底的,包括我們要看法的文件I/O,stream這個類有兩個重要的運算符:
1、拔出器(<<)
向流輸入數據。比方說零碎有一個默許的規范輸入流(cout),普通狀況下就是指的顯示器,所以,cout
2、析取器(>>)
從流中輸出數據。比方說零碎有一個默許的規范輸出流(cin),普通狀況下就是指的鍵盤,所以,cin>>x;
就表示從規范輸出流中讀取一個指定類型(即變量x的類型)的數據。
在C++中,對文件的操作是經過stream的子類fstream(file stream)
來完成的,所以,要用這種方式操作文件,就必需參加頭文件fstream.h。
上面就把此類的文件操作進程逐個道來。
一、翻開文件
在fstream類中,有一個成員函數open()
,就是用來翻開文件的,其原型是:
void open(const char* filename,int mode,int access);
參數:
filename: 要翻開的文件名
mode: 要翻開文件的方式
access: 翻開文件的屬性
翻開文件的方式在類ios(是一切流式I/O類的基類)中定義,常用的值如下:
ios::app: 以追加的方式翻開文件
ios::ate: 文件翻開後定位到文件尾,ios:app
就包括有此屬性
ios::binary: 以二進制方式翻開文件,缺省的方式是文本方式。兩種方式的區別見前文
ios::in: 文件以輸出方式翻開
ios::out: 文件以輸入方式翻開
ios::nocreate: 不樹立文件,所以文件不存在時翻開失敗
ios::noreplace:不掩蓋文件,所以翻開文件時假如文件存在失敗
ios::trunc: 假如文件存在,把文件長度設為0
可以用“或”把以上屬性銜接起來,如ios::out|ios::binary
翻開文件的屬性取值是:
0:普通文件,翻開訪問
1:只讀文件
2:隱含文件
4:零碎文件
可以用“或”或許“+”把以上屬性銜接起來 ,如3或1|2就是以只讀和隱含屬性翻開文件。
例如:以二進制輸出方式翻開文件c:config.sys
fstream file1; file1.open("c:config.sys",ios::binary|ios::in,0);
假如open函數只要文件名一個參數,則是以讀/寫普通文件翻開,即:
file1.open("c:config.sys");<=>file1.open("c:config.sys",ios::in|ios::out,0);
另外,fstream還有和open()
一樣的結構函數,關於上例,在定義的時侯就可以翻開文件了:
fstream file1("c:config.sys");
特別提出的是,fstream有兩個子類:ifstream(input file stream)
和ofstream(outpu file stream)
,ifstream默許以輸出方式翻開文件,而ofstream默許以輸入方式翻開文件。
ifstream file2("c:pdos.def");//以輸出方式翻開文件 ofstream file3("c:x.123");//以輸入方式翻開文件
所以,在實踐使用中,依據需求的不同,選擇不同的類來定義:假如想以輸出方式翻開,就用ifstream來定義;假如想以輸入方式翻開,就用ofstream來定義;假如想以輸出/輸入方式來翻開,就用fstream來定義。
二、封閉文件
翻開的文件運用完成後一定要封閉,fstream提供了成員函數close()來完成此操作,如:file1.close();就把file1相連的文件封閉。
三、讀寫文件
讀寫文件分為文本文件和二進制文件的讀取,關於文本文件的讀取比擬復雜,用拔出器和析取器就可以了;而關於二進制的讀取就要復雜些,下要就詳細的引見這兩種方式
1、文本文件的讀寫
文本文件的讀寫很復雜:用拔出器(>)從文件輸出。假定file1是以輸出方式翻開,file2以輸入翻開。示例如下:
file2"I Love You";//向文件寫入字符串"I Love You" int i; file1>>i;//從文件輸出一個整數值。
這種方式還有一種復雜的格式化才能,比方可以指定輸入為16進制等等,詳細的格式有以下一些
操縱符 功用 輸出/輸入
dec 格式化為十進制數值數據 輸出和輸入
endl 輸入一個換行符並刷新此流 輸入
ends 輸入一個空字符 輸入
hex 格式化為十六進制數值數據 輸出和輸入
oct 格式化為八進制數值數據 輸出和輸入
setpxecision(int p) 設置浮點數的精度位數 輸入
比方要把123當作十六進制輸入:file1<<hex<<123;要把3.1415926以5位精度輸入:file1<<setpxecision(5)<<3.1415926。
2、二進制文件的讀寫
①put()
put()
函數向流寫入一個字符,其原型是ofstream &put(char ch)
,運用也比擬復雜,如file1.put(‘c');
就是向流寫一個字符'c'。
②get()
get()
函數比擬靈敏,有3種常用的重載方式:
一種就是和put()
對應的方式:ifstream &get(char &ch);
功用是從流中讀取一個字符,後果保管在援用ch中,假如到文件尾,前往空字符。如file2.get(x);
表示從文件中讀取一個字符,並把讀取的字符保管在x中。
另一種重載方式的原型是: int get();
這種方式是從流中前往一個字符,假如抵達文件尾,前往EOF,如x=file2.get();
和上例功用是一樣的。
還 有一種方式的原型是:ifstream &get(char *buf,int num,char delim='n');
這種方式把字符讀入由 buf 指向的數組,直到讀入了 num 個字符或遇到了由 delim 指定的字符,假如沒運用 delim 這個參數,將運用缺省值換行符'n'。
例如:
file2.get(str1,127,'A');//從文件中讀取字符到字符串str1,當遇到字符'A'或讀取了127個字符時終止。
③讀寫數據塊
要讀寫二進制數據塊,運用成員函數read()
和write()
成員函數,它們原型如下:
read(unsigned char *buf,int num); write(const unsigned char *buf,int num);
read()
從文件中讀取 num 個字符到 buf 指向的緩存中,假如在還未讀入 num 個字符時就到了文件尾,可以用成員函數 int gcount();
來獲得實踐讀取的字符數;而 write()
從buf 指向的緩存寫 num 個字符到文件中,值得留意的是緩存的類型是 unsigned char *,有時能夠需求類型轉換。
例:
unsigned char str1[]="I Love You"; int n[5]; ifstream in("xxx.xxx"); ofstream out("yyy.yyy"); out.write(str1,strlen(str1));//把字符串str1全部寫到yyy.yyy中 in.read((unsigned char*)n,sizeof(n)); //從xxx.xxx中讀取指定個整數,留意類型轉換 in.close();out.close();
四、檢測 EOF
成員函數eof()用來檢測能否抵達文件尾,假如抵達文件尾前往非0值,否則前往0。原型是int eof();
例:
if(in.eof())ShowMessage("曾經抵達文件尾!");
五、文件定位
和 C的文件操作方式不同的是,C++ I/O零碎管理兩個與一個文件相聯絡的指針。一個是讀指針,它闡明輸出操作在文件中的地位;另一個是寫指針,它下次寫操作的地位。每次執行輸出或輸入時, 相應的指針自動變化。所以,C++的文件定位分為讀地位和寫地位的定位,對應的成員函數是 seekg()
和 seekp()
, seekg()
是設置讀地位,seekp是設置寫地位。它們最通用的方式如下:
istream &seekg(streamoff offset,seek_dir origin); ostream &seekp(streamoff offset,seek_dir origin);
streamoff定義於 iostream.h 中,定義有偏移量 offset 所能獲得的最大值,seek_dir 表示挪動的基准地位,是一個有以下值的枚舉:
ios::beg
: 文件掃尾
ios::cur
: 文件以後地位
ios::end
: 文件開頭
這兩個函數普通用於二進制文件,由於文本文件會由於零碎對字符的解釋而能夠與料想的值不同。
例:
file1.seekg(1234,ios::cur);//把文件的讀指針從以後地位向後移1234個字節 file2.seekp(1234,ios::beg);//把文件的寫指針從文件掃尾向後移1234個字節
總結
以上就是這篇文章的全部內容了,希望本文的內容對大家的學習或許任務能帶來一定的協助,假如有疑問大家可以留言交流。