詳解C++編程中對二進制文件的讀寫操作。本站提示廣大學習愛好者:(詳解C++編程中對二進制文件的讀寫操作)文章只能為提供參考,不一定能成為您想要的結果。以下是詳解C++編程中對二進制文件的讀寫操作正文
二進制文件不是以ASCII代碼寄存數據的,它將內存中數據存儲情勢不加轉換地傳送到磁盤文件,是以它又稱為內存數據的映像文件。由於文件中的信息不是字符數據,而是字節中的二進制情勢的信息,是以它又稱為字節文件。
對二進制文件的操作也須要先翻開文件,用完後要封閉文件。在翻開時要用ios::binary指定為以二進制情勢傳送和存儲。二進制文件除可以作為輸出文件或輸入文件外,還可所以既能輸出又能輸入的文件。這是和ASCII文件分歧的處所。
用成員函數read和write讀寫二進制文件
對二進制文件的讀寫重要用istream類的成員函數read和write來完成。這兩個成員函數的原型為
istream& read(char *buffer,int len); ostream& write(const char * buffer,int len);
字符指針buffer指向內存中一段存儲空間。len是讀寫的字節數。挪用的方法為:
a. write(p1,50); b. read(p2,30);
下面第一行中的a是輸入文件流對象,write函數將字符指針p1所給出的地址開端的50個字節的內容不加轉換地寫到磁盤文件中。在第二行中,b是輸出文件流對象,read 函數從b所聯系關系的磁盤文件中,讀入30個字節(或遇EOF停止),寄存在字符指針p2所指的一段空間內。
[例] 將一批數據以二進制情勢寄存在磁盤文件中。
#include <fstream> using namespace std; struct student { char name[20]; int num; int age; char sex; }; int main( ) { student stud[3]={"Li",1001,18,'f',"Fun",1002,19,'m',"Wang",1004,17,'f'}; ofstream outfile("stud.dat",ios::binary); if(!outfile) { cerr<<"open error!"<<endl; abort( );//加入法式 } for(int i=0;i<3;i++) outfile.write((char*)&stud[i],sizeof(stud[i])); outfile.close( ); return 0; }
用成員函數write向stud.dat輸入數據,早年面給出的write函數的原型可以看出: 第1個形參是指向char型常變量的指針變量buffer,之所以用const聲明,是由於不許可經由過程指針轉變其指向數據的值。形參請求響應的實參是字符指針或字符串的首地址。如今要將構造體數組的一個元素(包括4個成員)一次輸入到磁盤文件stud.dat。&tud[i] 是構造體數組第i個元素的首地址,但這是指向構造體的指針,與形參類型不婚配。是以 要用(char *)把它強迫轉換為字符指針。第2個參數是指定一次輸入的字節數。sizeof (stud[i])的值是構造體數組的一個元素的字節數。挪用一次write函數,就將從&tud[i]開端的構造體數組的一個元素輸入到磁盤文件中,履行3次輪回輸入構造體數組的3個元素。
其實可以一次輸入構造體數組的個元素,將for輪回的兩行改成以下一行:
outfile.write((char*)&stud[0],sizeof(stud));
履行一次write函數即輸入了卻構體數組的全體數據。
abort函數的感化是加入法式,與exit感化雷同。
可以看到,用這類辦法一次可以輸入一批數據,效力較高。在輸入的數據之間不用參加空格,在一次輸入以後也不用加回車換行符。在今後從該文件讀入數據時不是靠空格作為數據的距離,而是用字節數來掌握。
[例] 將適才以二進制情勢寄存在磁盤文件中的數據讀入內存並在顯示器上顯示。
#include <fstream> using namespace std; struct student { string name; int num; int age; char sex; }; int main( ) { student stud[3]; int i; ifstream infile("stud.dat",ios::binary); if(!infile) { cerr<<"open error!"<<endl; abort( ); } for(i=0;i<3;i++) infile.read((char*)&stud[i],sizeof(stud[i])); infile.close( ); for(i=0;i<3;i++) { cout<<"NO."<<i+1<<endl; cout<<"name:"<<stud[i].name<<endl; cout<<"num:"<<stud[i].num<<endl;; cout<<"age:"<<stud[i].age<<endl; cout<<"sex:"<<stud[i].sex<<endl<<endl; } return 0; }
運轉時在顯示器上顯示:
NO.1 name: Li num: 1001 age: 18 sex: f NO.2 name: Fun num: 1001 age: 19 sex: m NO.3 name: Wang num: 1004 age: 17 sex: f
請思慮,可否一次讀入文件中的全體數據,如:
infile.read((char*)&stud[0],sizeof(stud));
謎底是可以的,將指定命目標字節讀入內存,順次寄存在以地址&tud[0]開端的存儲空間中。要留意讀入的數據的格局要與寄存它的空間的格局婚配。因為磁盤文件中的數據是從內存中構造體數組元素得來的,是以它依然保存構造體元素的數據格局。如今再讀入內存,寄存在異樣的構造體數組中,這必定是婚配的。假如把它放到一個整型數組中,就不婚配了,會失足。
與文件指針有關的流成員函數
在磁盤文件中有一個文件指針,用來指明以後應停止讀寫的地位。在輸出時每讀入 一個宇節,指針就向後挪動一個字節。在輸入時每向文件輸入一個字節,指針就向後挪動 一個字節,跟著輸入文件中字節赓續增長,指針赓續後移。關於二進制文件,許可對指針停止掌握,使它按用戶的意圖挪動到所需的地位,以便在該地位長進行讀寫。文件流供給 一些有關文件指針的成員函數。為了查閱便利,將它們歸結為下表:
幾點解釋:
1) 這些函數名的第一個字母或最初一個字母不是g就是p。帶 g的是用於輸出的函數(g是get的第一個字母,以g作為輸出的標識,輕易懂得和記憶), 帶p的是用於輸入的函數(P是put的第一個字母,以P作為輸入的標識)。例若有兩個 tell 函數,tellg用於輸出文件,tellp用於輸入文件。異樣,seekg用於輸出文件,seekp用於輸入文件。以上函數見名知意,一看就明確,不用逝世記。
假如是既可輸出又可輸入的文件,則隨意率性用seekg或seekp。
2) 函數參數中的“文件中的地位”和“位移量”已被指定為long型整數,以字節為單元。“參照地位”可所以上面三者之一:
ios::beg 文件開首(beg是begin的縮寫),這是默許值。
ios::cur 指針以後的地位(cur是current的縮寫)。
ios::end 文件末尾。
它們是在ios類中界說的列舉常量。舉例以下:
infile.seekg(100); //輸出文件中的指針向前移到字節地位
infile.seekg(-50,ios::cur); //輸出文件中的指針從以後地位後移字節
outfile.seekp(-75,ios::end); //輸入文件中的指針從文件尾後移字節
隨機拜訪二進制數據文件
普通情形下讀寫是次序停止的,即逐一字節停止讀寫。然則關於二進制數據文件來講,可以應用下面的成員函數挪動指針,隨機地拜訪文件中任一名置上的數據,還可以修正文件中的內容。
[例] 有個先生的數據,請求:
把它們存到磁盤文件中;
將磁盤文件中的第,3,5個先生數據讀入法式,並顯示出來;
將第個先生的數據修正後存回磁盤文件中的原有地位。
從磁盤文件讀入修正後的個先生的數據並顯示出來。
要完成以上請求,須要處理個成績:
因為統一磁盤文件在法式中須要頻仍地停止輸出和輸入,是以可將文件的任務方法指定為輸出輸入文件,即ios::in|ios::out|ios::binary。
准確盤算好每次拜訪時指針的定位,即准確應用seekg或seekp函數。
准確停止文件中數據的重寫(更新)。
可寫出以下法式:
#include <fstream> using namespace std; struct student { int num; char name[20]; float score; }; int main( ) { student stud[5]={1001,"Li",85,1002,"Fun",97.5,1004,"Wang",54,1006,"Tan",76.5,1010,"ling",96}; fstream iofile("stud.dat",ios::in|ios::out|ios::binary); //用fstream類界說輸出輸入二進制文件流對象iofile if(!iofile) { cerr<<"open error!"<<endl; abort( ); } for(int i=0;i<5;i++) //向磁盤文件輸入個先生的數據 iofile.write((char *)&stud[i],sizeof(stud[i])); student stud1[5]; //用來寄存從磁盤文件讀入的數據 for(int i=0;i<5;i=i+2) { iofile.seekg(i*sizeof(stud[i]),ios::beg); //定位於第,2,4先生數據開首 //前後讀入個先生的數據,寄存在stud1[0],stud[1]和stud[2]中 iofile.read((char *)&stud1[i/2],sizeof(stud1[0])); //輸入stud1[0],stud[1]和stud[2]各成員的值 cout<<stud1[i/2].num<<" "<<stud1[i/2].name<<" "<<stud1[i/2].score<<endl; } cout<<endl; stud[2].num=1012; //修正第個先生(序號為)的數據 strcpy(stud[2].name,"Wu"); stud[2].score=60; iofile.seekp(2*sizeof(stud[0]),ios::beg); //定位於第個先生數據的開首 iofile.write((char *)&stud[2],sizeof(stud[2])); //更新第個先生數據 iofile.seekg(0,ios::beg); //從新定位於文件開首 for(int i=0;i<5;i++) { iofile.read((char *)&stud[i],sizeof(stud[i])); //讀入個先生的數據 cout<<stud[i].num<<" "<<stud[i].name<<" "<<stud[i].score<<endl; } iofile.close( ); return 0; }
運轉情形以下:
1001 Li 85(第個先生數據) 1004 Wang 54 (第個先生數據) 1010 ling 96 (第個先生數據) 1001 Li 85 (輸入修正後個先生數據) 1002 Fun 97.5 1012 Wu 60 (已修正的第個先生數據) 1006 Tan 76.5 1010 ling 96
本法式也能夠將磁盤文件stud.dat前後界說為輸入文件和輸出文件,在停止第一次的輸入以後封閉該文件,然後再按輸出方法翻開它,輸出完後再封閉它,然後再按輸入方法翻開,再封閉,再按輸出方法翻開它,輸出完後再封閉。明顯這是很煩瑣和不便利的。 在法式中把它指定為輸出輸入型的二進制文件。如許,不只可以向文件添加新的數據或讀入數據,還可以修正(更新)數據。應用這些功效,可以完成比擬龐雜的輸出輸入義務。
請留意,不克不及用ifstream或ofstream類界說輸出輸入的二進制文件流對象,而應該用fstream類。