C++檔案操作詳解

來源:互聯網
上載者:User

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<<"Write Stdout"<<'n';就表示把字串"Write Stdout"和換行字元('n')輸出到標準輸出資料流。

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 輸出一個Null 字元 輸出
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中,如果到檔案尾,返回Null 字元。如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個位元組
相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.