C++用read()和write()讀寫(xiě)二進(jìn)制文件的超詳細(xì)教程
前言
通過(guò)前一節(jié)的學(xué)習(xí),讀者了解了以文本形式讀寫(xiě)文件和以二進(jìn)制形式讀寫(xiě)文件的區(qū)別,并掌握了用重載的 >> 和 << 運(yùn)算符實(shí)現(xiàn)以文本形式讀寫(xiě)文件。在此基礎(chǔ)上,本節(jié)繼續(xù)講解如何以二進(jìn)制形式讀寫(xiě)文件。
不過(guò)介紹具體的實(shí)現(xiàn)方法前,先給讀者介紹一下相比以文本形式讀寫(xiě)文件,以二進(jìn)制形式讀寫(xiě)文件有哪些好處?
舉個(gè)例子,現(xiàn)在要做一個(gè)學(xué)籍管理程序,其中一個(gè)重要的工作就是記錄學(xué)生的學(xué)號(hào)、姓名、年齡等信息。這意味著,我們需要用一個(gè)類來(lái)表示學(xué)生,如下所示:
class CStudent { char szName[20]; //假設(shè)學(xué)生姓名不超過(guò)19個(gè)字符,以 '\0' 結(jié)尾 char szId[l0]; //假設(shè)學(xué)號(hào)為9位,以 '\0' 結(jié)尾 int age; //年齡 };
前面章節(jié)中,我們學(xué)會(huì)了如何以文本形式讀寫(xiě)文件,如果使用此方式存儲(chǔ)學(xué)生的信息,則最終的文件中存儲(chǔ)的學(xué)生信息可能是這個(gè)樣子:
Micheal Jackson 110923412 17
Tom Hanks 110923413 18
…
要知道,這種存儲(chǔ)學(xué)生信息的方式不但浪費(fèi)空間,而且后期不利于查找指定學(xué)生的信息(查找效率低下),因?yàn)槊總€(gè)學(xué)生的信息所占用的字節(jié)數(shù)不同。
這種情況下,以二進(jìn)制形式將學(xué)生信息存儲(chǔ)到文件中,是非常不錯(cuò)的選擇,因?yàn)橐源诵问酱鎯?chǔ)學(xué)生信息,可以直接把 CStudent 對(duì)象寫(xiě)入文件中,這意味著每個(gè)學(xué)生的信息都只占用 sizeof(CStudent) 個(gè)字節(jié)。
值得一提的是,要實(shí)現(xiàn)以二進(jìn)制形式讀寫(xiě)文件,<< 和 >> 將不再適用,需要使用 C++ 標(biāo)準(zhǔn)庫(kù)專門提供的 read() 和 write() 成員方法。其中,read() 方法用于以二進(jìn)制形式從文件中讀取數(shù)據(jù);write() 方法用于以二進(jìn)制形式將數(shù)據(jù)寫(xiě)入文件。
C++ ostream::write()方法寫(xiě)文件
ofstream 和 fstream 的 write() 成員方法實(shí)際上繼承自 ostream 類,其功能是將內(nèi)存中 buffer 指向的 count 個(gè)字節(jié)的內(nèi)容寫(xiě)入文件,基本格式如下:
ostream & write(char* buffer, int count);
其中,buffer 用于指定要寫(xiě)入文件的二進(jìn)制數(shù)據(jù)的起始位置;count 用于指定寫(xiě)入字節(jié)的個(gè)數(shù)。
也就是說(shuō),該方法可以被 ostream 類的 cout 對(duì)象調(diào)用,常用于向屏幕上輸出字符串。同時(shí),它還可以被 ofstream 或者 fstream 對(duì)象調(diào)用,用于將指定個(gè)數(shù)的二進(jìn)制數(shù)據(jù)寫(xiě)入文件。
同時(shí),該方法會(huì)返回一個(gè)作用于該函數(shù)的引用形式的對(duì)象。舉個(gè)例子,obj.write() 方法的返回值就是對(duì) obj 對(duì)象的引用。
需要注意的一點(diǎn)是,write() 成員方法向文件中寫(xiě)入若干字節(jié),可是調(diào)用 write() 函數(shù)時(shí)并沒(méi)有指定這些字節(jié)寫(xiě)入文件中的具體位置。事實(shí)上,write() 方法會(huì)從文件寫(xiě)指針指向的位置將二進(jìn)制數(shù)據(jù)寫(xiě)入。所謂文件寫(xiě)指針,是是 ofstream 或 fstream 對(duì)象內(nèi)部維護(hù)的一個(gè)變量,文件剛打開(kāi)時(shí),文件寫(xiě)指針指向的是文件的開(kāi)頭(如果以 ios::app 方式打開(kāi),則指向文件末尾),用 write() 方法寫(xiě)入 n 個(gè)字節(jié),寫(xiě)指針指向的位置就向后移動(dòng) n 個(gè)字節(jié)。
下面的程序演示了如何將學(xué)生信息以二進(jìn)制形式寫(xiě)入文件:
#include <iostream> #include <fstream> using namespace std; class CStudent { public: char szName[20]; int age; }; int main() { CStudent s; ofstream outFile("students.dat", ios::out | ios::binary); while (cin >> s.szName >> s.age) outFile.write((char*)&s, sizeof(s)); outFile.close(); return 0; }
輸入:
Tom 60↙
Jack 80↙
Jane 40↙
^Z↙
其中,↙表示輸出換行符,^Z 表示輸入Ctrl+Z組合鍵結(jié)束輸入。
執(zhí)行程序后,會(huì)自動(dòng)生成一個(gè) students.dat 文件,其內(nèi)部存有 72 字節(jié)的數(shù)據(jù),如果用“記事本”打開(kāi)此文件,可能看到如下亂碼:
Tom 燙燙燙燙燙燙燙燙< Jack 燙燙燙燙燙燙燙蘌 Jane 燙燙燙燙燙燙燙?
值得一提的是,程序中第 13 行指定文件的打開(kāi)模式為 ios::out | ios::binary,即以二進(jìn)制寫(xiě)模式打開(kāi)。在 Windows平臺(tái)中,以二進(jìn)制模式打開(kāi)文件是非常有必要的,否則可能出錯(cuò)。
另外,第 15 行將 s 對(duì)象寫(xiě)入文件。s 的地址就是要寫(xiě)入文件的內(nèi)存緩沖區(qū)的地址,但是 &s 不是 char * 類型,因此要進(jìn)行強(qiáng)制類型轉(zhuǎn)換;第 16 行,文件使用完畢一定要關(guān)閉,否則程序結(jié)束后文件的內(nèi)容可能不完整。
C++ istream::read()方法讀文件
ifstream 和 fstream 的 read() 方法實(shí)際上繼承自 istream 類,其功能正好和 write() 方法相反,即從文件中讀取 count 個(gè)字節(jié)的數(shù)據(jù)。該方法的語(yǔ)法格式如下:
istream & read(char* buffer, int count);
其中,buffer 用于指定讀取字節(jié)的起始位置,count 指定讀取字節(jié)的個(gè)數(shù)。同樣,該方法也會(huì)返回一個(gè)調(diào)用該方法的對(duì)象的引用。
和 write() 方法類似,read() 方法從文件讀指針指向的位置開(kāi)始讀取若干字節(jié)。所謂文件讀指針,可以理解為是 ifstream 或 fstream 對(duì)象內(nèi)部維護(hù)的一個(gè)變量。文件剛打開(kāi)時(shí),文件讀指針指向文件的開(kāi)頭(如果以 ios::app 方式打開(kāi),則指向文件末尾),用 read() 方法讀取 n 個(gè)字節(jié),讀指針指向的位置就向后移動(dòng) n 個(gè)字節(jié)。因此,打開(kāi)一個(gè)文件后連續(xù)調(diào)用 read() 方法,就能將整個(gè)文件的內(nèi)容讀取出來(lái)。
通過(guò)執(zhí)行 write() 方法的示例程序,我們將 3 個(gè)學(xué)生的信息存儲(chǔ)到了 students.dat 文件中,下面程序演示了如何使用 read() 方法將它們讀取出來(lái):
#include <iostream> #include <fstream> using namespace std; class CStudent { public: char szName[20]; int age; }; int main() { CStudent s; ifstream inFile("students.dat",ios::in|ios::binary); //二進(jìn)制讀方式打開(kāi) if(!inFile) { cout << "error" <<endl; return 0; } while(inFile.read((char *)&s, sizeof(s))) { //一直讀到文件結(jié)束 cout << s.szName << " " << s.age << endl; } inFile.close(); return 0; }
程序的輸出結(jié)果是:
Tom 60
Jack 80
Jane 40
注意,程序中第 18 行直接將 read() 方法作為 while 循環(huán)的判斷條件,這意味著,read() 方法會(huì)一直讀取到文件的末尾,將所有字節(jié)全部讀取完畢,while 循環(huán)才會(huì)終止。
另外,在使用 read() 方法的同時(shí),如果想知道一共成功讀取了多少個(gè)字節(jié)(讀到文件尾時(shí),未必能讀取 count 個(gè)字節(jié)),可以在 read() 方法執(zhí)行后立即調(diào)用文件流對(duì)象的 gcount() 成員方法,其返回值就是最近一次 read() 方法成功讀取的字節(jié)數(shù)。感興趣的讀者可自行嘗試,這里不再做具體演示。
總結(jié)
到此這篇關(guān)于C++用read()和write()讀寫(xiě)二進(jìn)制文件的文章就介紹到這了,更多相關(guān)C++ read()和write()讀寫(xiě)二進(jìn)制文件內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
OpenCV實(shí)現(xiàn)相機(jī)標(biāo)定板
這篇文章主要為大家詳細(xì)介紹了OpenCV實(shí)現(xiàn)相機(jī)標(biāo)定板,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-04-04C語(yǔ)言實(shí)現(xiàn)求梅森素?cái)?shù)的代碼與解析
這篇文章主要給大家介紹了關(guān)于利用C語(yǔ)言實(shí)現(xiàn)求梅森素?cái)?shù)的代碼與解析,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2018-12-12運(yùn)用指針在不用加號(hào)的情況進(jìn)行加法運(yùn)算的講解
今天小編就為大家分享一篇關(guān)于運(yùn)用指針在不用加號(hào)的情況進(jìn)行加法運(yùn)算的講解,小編覺(jué)得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來(lái)看看吧2019-01-01C語(yǔ)言字符函數(shù)isalnum()和iscntrl()詳解
大家好,本篇文章主要講的是C語(yǔ)言字符函數(shù)isalnum()和iscntrl()詳解,感興趣的同學(xué)趕快來(lái)看一看吧,對(duì)你有幫助的話記得收藏一下2022-02-02C++實(shí)現(xiàn)LeetCode(兩個(gè)有序數(shù)組的中位數(shù))
這篇文章主要介紹了C++實(shí)現(xiàn)LeetCode(兩個(gè)有序數(shù)組的中位數(shù)),本篇文章通過(guò)簡(jiǎn)要的案例,講解了該項(xiàng)技術(shù)的了解與使用,以下就是詳細(xì)內(nèi)容,需要的朋友可以參考下2021-07-07C語(yǔ)言關(guān)于時(shí)間復(fù)雜度詳解
大家好,本篇文章主要講的是C語(yǔ)言關(guān)于時(shí)間復(fù)雜度詳解,感興趣的同學(xué)趕快來(lái)看一看吧,對(duì)你有幫助的話記得收藏一下,方便下次瀏覽2022-01-01