系統(tǒng)軟件園 - 專注分享最好的電腦系統(tǒng)軟件!免費(fèi)安全下載 系統(tǒng)軟件園首頁 | Win7激活工具 | 熱門專題
系統(tǒng)軟件園>您的位置:首頁 > > 系統(tǒng)教程 > 軟件教程 >

C++ 二進(jìn)制文件讀取和寫入(精選教程)

更新時(shí)間:2018-12-31 15:22:27| 編輯:本站整理 | 信息來源:本站整理|瀏覽數(shù):
在C++的編程中,經(jīng)常需要對(duì)二進(jìn)制文件進(jìn)行讀寫操作,對(duì)于初學(xué)者而言,可能代碼方面會(huì)有一些不熟悉,下面小編整理了C++ 二進(jìn)制文件讀取和寫入(精選教程),大家可以學(xué)習(xí)一下。

二進(jìn)制文件的讀寫對(duì)于普通文本要稍微麻煩一些,對(duì)二進(jìn)制文件的讀寫同樣需要打開文件和關(guān)閉文件,打開和關(guān)閉方式與文本文件相同,只不過需要在打開方式上加上ios::binary以指明以二進(jìn)制方式進(jìn)行讀寫。


C++二進(jìn)制文件讀取和寫入


C++二進(jìn)制文件讀寫對(duì)比文本文件的好處:


使用文本方式儲(chǔ)存信息比較我浪費(fèi)空間,也不便于檢索,如:一個(gè)學(xué)籍管理程序需要記錄所有學(xué)生的學(xué)號(hào)、姓名、年齡信息,并且能夠按照姓名查找學(xué)生的信息。程序中可以用一個(gè)類來表示學(xué)生:

  1. class CStudent
  2. {
  3.     char szName[20];  //假設(shè)學(xué)生姓名不超過19個(gè)字符,以 '\0' 結(jié)尾
  4.     char szId[l0];  //假設(shè)學(xué)號(hào)為9位,以 '\0' 結(jié)尾
  5.     int age;  //年齡
  6. };
如果用文本文件存儲(chǔ)學(xué)生的信息,文件可能是如下樣子:

Micheal Jackson 110923412 17

Tom Hanks 110923413 18



這種存儲(chǔ)方式不但浪費(fèi)空間,而且查找效率低下。因?yàn)槊總€(gè)學(xué)生的信息所占用的字節(jié)數(shù)不同,所以即使文件中的學(xué)生信息是按姓名排好序的,要用程序根據(jù)名字進(jìn)行查找仍然沒有什么好辦法,只能在文件中從頭到尾搜索。

如果把全部的學(xué)生信息都讀入內(nèi)存并排序后再查找,當(dāng)然速度會(huì)很快,但如果學(xué)生數(shù)巨大,則把所有學(xué)生信息都讀人內(nèi)存可能是不現(xiàn)實(shí)的。



可以用二進(jìn)制的方式來存儲(chǔ)學(xué)生信息,即把 CStudent 對(duì)象直接寫入文件。在該文件中,每個(gè)學(xué)生的信息都占用 sizeof(CStudent) 個(gè)字節(jié)。對(duì)象寫入文件后一般稱作“記錄”。本例中,每個(gè)學(xué)生都對(duì)應(yīng)于一條記錄。該學(xué)生記錄文件可以按姓名排序,則使用折半查找的效率會(huì)很高。

讀寫二進(jìn)制文件不能使用前面提到的類似于 cin、cout 從流中讀寫數(shù)據(jù)的方法。這時(shí)可以調(diào)用 ifstream 類和 fstream 類的 read 成員函數(shù)從文件中讀取數(shù)據(jù),調(diào)用 ofstream 和 fstream 的 write 成員函數(shù)向文件中寫入數(shù)據(jù)。


用 ostream::write 成員函數(shù)寫文件


ofstream 和 fstream 的 write 成員函數(shù)實(shí)際上繼承自 ostream 類,原型如下:

ostream & write(char* buffer, int count);

該成員函數(shù)將內(nèi)存中 buffer 所指向的 count 個(gè)字節(jié)的內(nèi)容寫入文件,返回值是對(duì)函數(shù)所作用的對(duì)象的引用,如 obj.write(...) 的返回值就是對(duì) obj 的引用。

write 成員函數(shù)向文件中寫入若干字節(jié),可是調(diào)用 write 函數(shù)時(shí)并沒有指定這若干字節(jié)要寫入文件中的什么位置。那么,write 函數(shù)在執(zhí)行過程中到底把這若干字節(jié)寫到哪里呢?答案是從文件寫指針指向的位置開始寫入。

文件寫指針是 ofstream 或 fstream 對(duì)象內(nèi)部維護(hù)的一個(gè)變量。文件剛打開時(shí),文件寫指針指向文件的開頭(如果以 ios::app 方式打開,則指向文件末尾),用 write 函數(shù)寫入 n 個(gè)字節(jié),寫指針指向的位置就向后移動(dòng) n 個(gè)字節(jié)。

下面的程序從鍵盤輸入幾名學(xué)生的姓名和年齡(輸入時(shí),在單獨(dú)的一行中按 Ctrl+Z 鍵再按回車鍵以結(jié)束輸入。假設(shè)學(xué)生姓名中都沒有空格),并以二進(jìn)制文件形式存儲(chǔ),成為一個(gè)學(xué)生記錄文件 students.dat。

例子,用二進(jìn)制文件保存學(xué)生記錄:

  1. #include <iostream>
  2. #include <fstream>
  3. using namespace std;
  4. class CStudent
  5. {
  6. public:
  7. char szName[20];
  8. int age;
  9. };
  10. int main()
  11. {
  12. CStudent s;
  13. ofstream outFile("students.dat", ios::out | ios::binary);
  14. while (cin >> s.szName >> s.age)
  15. outFile.write((char*)&s, sizeof(s));
  16. outFile.close();
  17. return 0;
  18. }
輸入:

Tom 60↙
Jack 80↙
Jane 40↙
^Z↙

則形成的 students.dat 為 72 字節(jié),用“記事本”程序打開呈現(xiàn)亂碼:

Tom燙燙燙燙燙燙燙燙 Jack燙燙燙燙燙燙燙? Jane燙燙燙燙燙燙燙?


第 13 行指定文件的打開模式是 ios::out|ios::binary,即以二進(jìn)制寫模式打開。在 Windows平臺(tái)中,用二進(jìn)制模式打開是必要的,否則可能出錯(cuò)。

第 15 行將 s 對(duì)象寫入文件。s 的地址就是要寫入文件的內(nèi)存緩沖區(qū)的地址。但是 &s 不是 char * 類型,因此要進(jìn)行強(qiáng)制類型轉(zhuǎn)換。

第 16 行,文件使用完畢一定要關(guān)閉,否則程序結(jié)束后文件的內(nèi)容可能不完整。


用 istream::read 成員函數(shù)讀文件


ifstream 和 fstream 的 read 成員函數(shù)實(shí)際上繼承自 istream 類,原型如下:

istream & read(char* buffer, int count);

該成員函數(shù)從文件中讀取 count 個(gè)字節(jié)的內(nèi)容,存放到 buffer 所指向的內(nèi)存緩沖區(qū)中,返回值是對(duì)函數(shù)所作用的對(duì)象的引用。

如果想知道一共成功讀取了多少個(gè)字節(jié)(讀到文件尾時(shí),未必能讀取 count 個(gè)字節(jié)),可以在 read 函數(shù)執(zhí)行后立即調(diào)用文件流對(duì)象的 gcount 成員函數(shù),其返回值就是最近一次 read 函數(shù)執(zhí)行時(shí)成功讀取的字節(jié)數(shù)。gcount 是 istream 類的成員函數(shù),原型如下:

int gcount();

read 成員函數(shù)從文件讀指針指向的位置開始讀取若干字節(jié)。文件讀指針是 ifstream 或 fstream 對(duì)象內(nèi)部維護(hù)的一個(gè)變量。文件剛打開時(shí),文件讀指針指向文件的開頭(如果以ios::app 方式打開,則指向文件末尾),用 read 函數(shù)讀取 n 個(gè)字節(jié),讀指針指向的位置就向后移動(dòng) n 個(gè)字節(jié)。因此,打開一個(gè)文件后連續(xù)調(diào)用 read 函數(shù),就能將整個(gè)文件的內(nèi)容讀取出來。

下面的程序?qū)⑶懊鎰?chuàng)建的學(xué)生記錄文件 students.dat 的內(nèi)容讀出并顯示。

  1. #include <iostream>
  2. #include <fstream>
  3. using namespace std;
  4. class CStudent
  5. {
  6. public:
  7. char szName[20];
  8. int age;
  9. };
  10. int main()
  11. {
  12. CStudent s;
  13. ifstream inFile("students.dat",ios::in|ios::binary); //二進(jìn)制讀方式打開
  14. if(!inFile) {
  15. cout << "error" <<endl;
  16. return 0;
  17. }
  18. while(inFile.read((char *)&s, sizeof(s))) { //一直讀到文件結(jié)束
  19. int readedBytes = inFile.gcount(); //看剛才讀了多少字節(jié)
  20. cout << s.szName << " " << s.age << endl;
  21. }
  22. inFile.close();
  23. return 0;
  24. }
程序的輸出結(jié)果是:
Tom 60
Jack 80
Jane 40

第 18 行,判斷文件是否已經(jīng)讀完的方法和 while(cin>>n) 類似,歸根到底都是因?yàn)?istream 類重載了 bool 強(qiáng)制類型轉(zhuǎn)換運(yùn)算符。

第 19 行只是演示 gcount 函數(shù)的用法,刪除該行對(duì)程序運(yùn)行結(jié)果沒有影響。

思考題:關(guān)于 students.dat 的兩個(gè)程序中,如果 CStudent 類的 szName 的定義不是“char szName[20] ”而是“string szName”,是否可以?為什么?


用文件流類的 put 和 get 成員函數(shù)讀寫文件

可以用 ifstream 和 fstream 類的 get 成員函數(shù)(繼承自 istream 類)從文件中一次讀取一個(gè)字節(jié),也可以用 ofstream 和 fstream 類的 put 成員函數(shù)(繼承自 ostream 類) 向文件中一次寫入一個(gè)字節(jié)。

例題:編寫一個(gè) mycopy 程序,實(shí)現(xiàn)文件復(fù)制的功能。用法是在“命令提示符”窗口輸入:

mycopy 源文件名 目標(biāo)文件名

就能將源文件復(fù)制到目標(biāo)文件。例如:

mycopy src.dat dest.dat

即將 src.dat 復(fù)制到 dest.dat。如果 dest.dat 原本就存在,則原來的文件會(huì)被覆蓋。

解題的基本思路是每次從源文件讀取一個(gè)字節(jié),然后寫入目標(biāo)文件。程序如下:

  1. #include <iostream>
  2. #include <fstream>
  3. using namespace std;
  4. int main(int argc, char* argv[])
  5. {
  6. if (argc != 3) {
  7. cout << "File name missing!" << endl;
  8. return 0;
  9. }
  10. ifstream inFile(argv[l], ios::binary | ios::in); //以二進(jìn)制讀模式打開文件
  11. if (!inFile) {
  12. cout << "Source file open error." << endl;
  13. return 0;
  14. }
  15. ofstream outFile(argv[2], ios::binary | ios::out); //以二進(jìn)制寫模式打開文件
  16. if (!outFile) {
  17. cout << "New file open error." << endl;
  18. inFile.close(); //打開的文件一定要關(guān)閉
  19. return 0;
  20. }
  21. char c;
  22. while (inFile.get(c)) //每次讀取一個(gè)字符
  23. outFile.put(c); //每次寫入一個(gè)字符
  24. outFile.close();
  25. inFile.close();
  26. return 0;
  27. }
文件存放于磁盤中,磁盤的訪問速度遠(yuǎn)遠(yuǎn)低于內(nèi)存。如果每次讀一個(gè)字節(jié)或?qū)懸粋€(gè)字節(jié)都要訪問磁盤,那么文件的讀寫速度就會(huì)慢得不可忍受。因此,操作系統(tǒng)在接收到讀文件的請(qǐng)求時(shí),哪怕只要讀一個(gè)字節(jié),也會(huì)把一片數(shù)據(jù)(通常至少是 512 個(gè)字節(jié),因?yàn)榇疟P的一個(gè)扇區(qū)是 512 B)都讀取到一個(gè)操作系統(tǒng)自行管理的內(nèi)存緩沖區(qū)中,當(dāng)要讀下一個(gè)字節(jié)時(shí),就不需要訪問磁盤,直接從該緩沖區(qū)中讀取就可以了。

操作系統(tǒng)在接收到寫文件的請(qǐng)求時(shí),也是先把要寫入的數(shù)據(jù)在一個(gè)內(nèi)存緩沖區(qū)中保存起來,等緩沖區(qū)滿后,再將緩沖區(qū)的內(nèi)容全部寫入磁盤。關(guān)閉文件的操作就能確保內(nèi)存緩沖區(qū)中的數(shù)據(jù)被寫入磁盤。

盡管如此,要連續(xù)讀寫文件時(shí),像 mycopy 程序那樣一個(gè)字節(jié)一個(gè)字節(jié)地讀寫,還是不如一次讀寫一片內(nèi)存區(qū)域快。每次讀寫的字節(jié)數(shù)最好是 512 的整數(shù)倍。




1. 二進(jìn)制文件寫入示例:

//采用C模式寫二進(jìn)制文件
void DataWrite_CMode()
{
	//準(zhǔn)備數(shù)據(jù)
	double pos[200];
	for(int i = 0; i < 200; i ++ )
		pos[i] = i ;
	//寫出數(shù)據(jù)
	FILE *fid;
	fid = fopen("binary.dat","wb");
	if(fid == NULL)
	{
		printf("寫出文件出錯(cuò)");
		return;
	}
	int mode = 1;
	printf("mode為1,逐個(gè)寫入;mode為2,逐行寫入\n");
	scanf("%d",&mode);
	if(1==mode)
	{
		for(int i = 0; i < 200; i++)
			fwrite(&pos[i],sizeof(double),1,fid);
	}
	else if(2 == mode)
	{
		fwrite(pos, sizeof(double), 200, fid);
	}
	fclose(fid);
}

2. 二進(jìn)制文件讀取示例:
//采用C模式讀二進(jìn)制文件
void DataRead_CMode()
{
	FILE *fid;
	fid = fopen("binary.dat","rb");
	if(fid == NULL)
	{
		printf("讀取文件出錯(cuò)");
		return;
	}
	int mode = 1;
	printf("mode為1,知道pos有多少個(gè);mode為2,不知道pos有多少個(gè)\n");
	scanf("%d",&mode);
	if(1 == mode)
	{
		double pos[200];
		fread(pos,sizeof(double),200,fid);
		for(int i = 0; i < 200; i++)
			printf("%lf\n", pos[i]);
		free(pos);
	}
	else if(2 == mode)
	{
		//獲取文件大小
		fseek (fid , 0 , SEEK_END);       
		long lSize = ftell (fid);  
		rewind (fid); 
		//開辟存儲(chǔ)空間
		int num = lSize/sizeof(double);
		double *pos = (double*) malloc (sizeof(double)*num);  
		if (pos == NULL)  
		{  
			printf("開辟空間出錯(cuò)");   
			return; 
		} 
		fread(pos,sizeof(double),num,fid);
		for(int i = 0; i < num; i++)
			printf("%lf\n", pos[i]);
		free(pos);     //釋放內(nèi)存
	}
	fclose(fid);
}

返回頂部


系統(tǒng)軟件園發(fā)布的系統(tǒng)鏡像及軟件均來至互聯(lián)網(wǎng),僅供學(xué)習(xí)和研究使用,不得用于任何商業(yè)用途并請(qǐng)?jiān)谙螺d后24小時(shí)內(nèi)刪除,如果滿意請(qǐng)聯(lián)系版權(quán)方購買。
如果您發(fā)現(xiàn)本站侵害了您的版權(quán),請(qǐng)立即聯(lián)系我們,本站將第一時(shí)間進(jìn)行相關(guān)處理。聯(lián)系方式(見首頁)
版權(quán)聲明|下載聲明 Copyright @ 2016 系統(tǒng)軟件園