首頁 > 軟體

C++用read()和write()讀寫二進位制檔案的超詳細教學

2022-06-07 14:00:28

前言

通過前一節的學習,讀者瞭解了以文字形式讀寫檔案和以二進位制形式讀寫檔案的區別,並掌握了用過載的 >> 和 << 運運算元實現以文字形式讀寫檔案。在此基礎上,本節繼續講解如何以二進位制形式讀寫檔案。

不過介紹具體的實現方法前,先給讀者介紹一下相比以文字形式讀寫檔案,以二進位制形式讀寫檔案有哪些好處?

舉個例子,現在要做一個學籍管理程式,其中一個重要的工作就是記錄學生的學號、姓名、年齡等資訊。這意味著,我們需要用一個類來表示學生,如下所示:

class CStudent
{
    char szName[20];  //假設學生姓名不超過19個字元,以 '' 結尾
    char szId[l0];  //假設學號為9位,以 '' 結尾
    int age;  //年齡
};

前面章節中,我們學會了如何以文字形式讀寫檔案,如果使用此方式儲存學生的資訊,則最終的檔案中儲存的學生資訊可能是這個樣子:

Micheal Jackson 110923412 17
Tom Hanks 110923413 18

要知道,這種儲存學生資訊的方式不但浪費空間,而且後期不利於查詢指定學生的資訊(查詢效率低下),因為每個學生的資訊所佔用的位元組數不同。

這種情況下,以二進位制形式將學生資訊儲存到檔案中,是非常不錯的選擇,因為以此形式儲存學生資訊,可以直接把 CStudent 物件寫入檔案中,這意味著每個學生的資訊都只佔用 sizeof(CStudent) 個位元組。

值得一提的是,要實現以二進位制形式讀寫檔案,<< 和 >> 將不再適用,需要使用 C++ 標準庫專門提供的 read() 和 write() 成員方法。其中,read() 方法用於以二進位制形式從檔案中讀取資料;write() 方法用於以二進位制形式將資料寫入檔案。

C++ ostream::write()方法寫檔案

ofstream 和 fstream 的 write() 成員方法實際上繼承自 ostream 類,其功能是將記憶體中 buffer 指向的 count 個位元組的內容寫入檔案,基本格式如下:

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

其中,buffer 用於指定要寫入檔案的二進位制資料的起始位置;count 用於指定寫入位元組的個數。

也就是說,該方法可以被 ostream 類的 cout 物件呼叫,常用於向螢幕上輸出字串。同時,它還可以被 ofstream 或者 fstream 物件呼叫,用於將指定個數的二進位制資料寫入檔案。

同時,該方法會返回一個作用於該函數的參照形式的物件。舉個例子,obj.write() 方法的返回值就是對 obj 物件的參照。

需要注意的一點是,write() 成員方法向檔案中寫入若干位元組,可是呼叫 write() 函數時並沒有指定這些位元組寫入檔案中的具體位置。事實上,write() 方法會從檔案寫指標指向的位置將二進位制資料寫入。所謂檔案寫指標,是是 ofstream 或 fstream 物件內部維護的一個變數,檔案剛開啟時,檔案寫指標指向的是檔案的開頭(如果以 ios::app 方式開啟,則指向檔案末尾),用 write() 方法寫入 n 個位元組,寫指標指向的位置就向後移動 n 個位元組。

下面的程式演示瞭如何將學生資訊以二進位制形式寫入檔案:

#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組合鍵結束輸入。

執行程式後,會自動生成一個 students.dat 檔案,其內部存有 72 位元組的資料,如果用“記事本”開啟此檔案,可能看到如下亂碼:

Tom 燙燙燙燙燙燙燙燙<   Jack 燙燙燙燙燙燙燙蘌   Jane 燙燙燙燙燙燙燙?

值得一提的是,程式中第 13 行指定檔案的開啟模式為 ios::out | ios::binary,即以二進位制寫模式開啟。在 Windows平臺中,以二進位制模式開啟檔案是非常有必要的,否則可能出錯。

另外,第 15 行將 s 物件寫入檔案。s 的地址就是要寫入檔案的記憶體緩衝區的地址,但是 &s 不是 char * 型別,因此要進行強制型別轉換;第 16 行,檔案使用完畢一定要關閉,否則程式結束後檔案的內容可能不完整。

C++ istream::read()方法讀檔案

ifstream 和 fstream 的 read() 方法實際上繼承自 istream 類,其功能正好和 write() 方法相反,即從檔案中讀取 count 個位元組的資料。該方法的語法格式如下:

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

其中,buffer 用於指定讀取位元組的起始位置,count 指定讀取位元組的個數。同樣,該方法也會返回一個呼叫該方法的物件的參照。

和 write() 方法類似,read() 方法從檔案讀指標指向的位置開始讀取若干位元組。所謂檔案讀指標,可以理解為是 ifstream 或 fstream 物件內部維護的一個變數。檔案剛開啟時,檔案讀指標指向檔案的開頭(如果以 ios::app 方式開啟,則指向檔案末尾),用 read() 方法讀取 n 個位元組,讀指標指向的位置就向後移動 n 個位元組。因此,開啟一個檔案後連續呼叫 read() 方法,就能將整個檔案的內容讀取出來。

通過執行 write() 方法的範例程式,我們將 3 個學生的資訊儲存到了 students.dat 檔案中,下面程式演示瞭如何使用 read() 方法將它們讀取出來:

#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); //二進位制讀方式開啟
    if(!inFile) {
        cout << "error" <<endl;
        return 0;
    }
    while(inFile.read((char *)&s, sizeof(s))) { //一直讀到檔案結束
        cout << s.szName << " " << s.age << endl;   
    }
    inFile.close();
    return 0;
}

程式的輸出結果是:

Tom 60
Jack 80
Jane 40

注意,程式中第 18 行直接將 read() 方法作為 while 迴圈的判斷條件,這意味著,read() 方法會一直讀取到檔案的末尾,將所有位元組全部讀取完畢,while 迴圈才會終止。

另外,在使用 read() 方法的同時,如果想知道一共成功讀取了多少個位元組(讀到檔案尾時,未必能讀取 count 個位元組),可以在 read() 方法執行後立即呼叫檔案流物件的 gcount() 成員方法,其返回值就是最近一次 read() 方法成功讀取的位元組數。感興趣的讀者可自行嘗試,這裡不再做具體演示。

總結

到此這篇關於C++用read()和write()讀寫二進位制檔案的文章就介紹到這了,更多相關C++ read()和write()讀寫二進位制檔案內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!


IT145.com E-mail:sddin#qq.com