這篇文章主要給大家深入的介紹了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("http://xxx.xxx");
ofstream out("yyy.yyy");
out.write(str1,strlen(str1));//把字元串str1全部寫到yyy.yyy中
in.read((unsigned char*)n,sizeof(n));
//從http://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個位元組
(文章出自腳本之家)
以上就是這篇文章的全部内容了,希望本文的内容對大家的學習或者工作能帶來一定的幫助,如果有疑問大家可以留言交流。
如果大家想要學習更多的話就關注小編吧!