1. 程式人生 > >C++ 學習筆記之——檔案操作和檔案流

C++ 學習筆記之——檔案操作和檔案流

1. 檔案的概念

對於使用者來說,常用到的檔案有兩大類:程式檔案和資料檔案。而根據檔案中資料的組織方式,則可以將檔案分為 ASCII 檔案二進位制檔案

  • ASCII 檔案,又稱字元檔案或者文字檔案,它的每一個位元組放一個 ASCII 程式碼,代表一個字元。
  • 二進位制檔案,又稱內部格式檔案或位元組檔案,是把記憶體中的資料按其在記憶體中的儲存形式原樣輸出到磁碟上存放。

數字 64 在記憶體中表示為 0100 0000,若將其儲存為 ASCII 檔案,則要分別存放十位 6 和個位 4 的 ASCII 碼,為 0011 0110 0011 0100,佔用兩個位元組;若將其儲存為二進位制檔案,則按記憶體中形式直接輸出,為 0100 0000,佔用一個位元組。

ASCII 檔案中資料與字元一一對應,一個位元組代表一個字元,可以直接在螢幕上顯示或打印出來,這種方式使用方便,比較直觀,便於閱讀,但一般佔用儲存空間較大,而且輸出時要將二進位制轉化為 ASCII 碼比較花費時間。

二進位制檔案,輸出時不需要進行轉化,直接將記憶體中的形式輸出到檔案中,佔用儲存空間較小,但一個位元組並不對應一個檔案,不能直觀顯示檔案中的內容。

2. 檔案流和檔案流物件

檔案流是以外存檔案未輸入輸出物件的資料流。輸出檔案流是從記憶體流向外存檔案的資料,輸入檔案流是從外存檔案流向記憶體的資料。每一個檔案流都有一個記憶體緩衝區與之對應。

C++ 中有三個用於檔案操作的檔案類:

  • ifstream 類,它是從 istream 類派生來的,用於支援從磁碟檔案的輸入。
  • ofstream 類,它是從 ostream 類派生來的,用於支援向磁碟檔案的輸出。
  • fstream 類,它是從 iostream 類派生來的,用於支援對磁碟檔案的輸入輸出。

要以磁碟檔案為物件進行輸入輸出,必須定義一個檔案流類的物件,通過檔案流物件將資料從記憶體輸出到磁碟檔案,或者將磁碟檔案輸入到記憶體。

定義檔案流物件後,我們還需要將檔案流物件和指定的磁碟檔案建立關聯,以便使檔案流流向指定的磁碟檔案,並確定檔案的工作方式(輸入還是輸出,二進位制還是 ASCII)。我們可以在定義流物件的時候指定引數來呼叫建構函式,或者通過成員函式 open 來進行檔案流物件和指定檔案的關聯。

3. 對 ASCII 檔案的操作

然後,我們就可以用類似 cin 或者 cout 的方式將資料讀出或寫入檔案,只不過是輸入輸出的物件變成了檔案而已。當然,在對磁碟檔案完成讀寫操作後,我們可以通過 close 方法來解除磁碟檔案和檔案流物件的關聯。

#include <iostream>
#include <fstream>

using namespace std;

int main()
{
    ofstream outfile("a.txt", ios::out);

    if (!outfile)
    {
        cerr << "Failed to open the file!";
        return 1;
    }

    // 寫入數字 1-5 到檔案中
    for (int i = 1; i < 6; i++)
    {
        outfile << i << '\n';
    }

    outfile.close();

    ifstream infile("a.txt", ios::in);

    if (!infile)
    {
        cerr << "Failed to open the file!";
        return 1;
    }

    char data;  // 從檔案中讀出數字 1-5 
    for (int i = 1; i < 6; i++)
    {
        infile >> data;
        cout << data << '\n';
    }

    infile.close();

    return 0;
}

也可以利用檔案流物件的成員函式 get, put 等,其用法就和 標準輸入輸出 介紹的一樣。

int main()
{
    ofstream outfile("a.txt", ios::out);

    if (!outfile)
    {
        cerr << "Failed to open the file!";
        return 1;
    }

    for (char i = '1'; i < '6'; i++)
    {
        outfile.put(i); // 輸出一個字元到檔案中去
    }

    outfile.close();

    ifstream infile("a.txt", ios::in);

    if (!infile)
    {
        cerr << "Failed to open the file!";
        return 1;
    }

    /*char a;
    for (int i = 0; i < 5; i++)
    {
        infile.get(a); // 從檔案中讀出 1 個字元
        cout << a << '\n';
    }*/

    char data[5];
    infile.get(data, 6); // 從檔案中讀出 5 個字元
    for (int i = 0; i < 5; i++)
    {
        cout << data[i] << '\n';
    }

    infile.close();

    return 0;
}

4. 對二進位制檔案的操作

二進位制檔案的操作需要在開啟檔案的時候指定開啟方式為 ios::binary,並且還可以指定為既能輸入又能輸出的檔案,我們通過成員函式 read 和 write 來讀寫二進位制檔案。

  • istream& read (char* s, streamsize n);
  • ostream& write (const char* s, streamsize n);
#include <iostream>
#include <fstream>

using namespace std;

int main()
{
    ofstream outfile("a.txt", ios::binary);

    if (!outfile)
    {
        cerr << "Failed to open the file!";
        return 1;
    }

    char a[] = {'h', 'e', 'l', 'l', 'o', ','};
    char b[] = {'s', 'e', 'n', 'i', 'u', 's', 'e', 'n', '!'};

    outfile.write(a, 6); // 將以 a 為首地址的 6 個字元寫入檔案
    outfile.write(b, 9);
    outfile.close();

    ifstream infile("a.txt", ios::binary);

    if (!infile)
    {
        cerr << "Failed to open the file!";
        return 1;
    }

    char data[6];
    infile.read(data, 6);  // 從檔案中讀出 6 個字元到以 data 為首地址的字元陣列中
    for (int i = 0; i < 6; i++)
    {
        cout << data[i];
    }

    char datb[6];
    infile.read(datb, 9);
    for (int i = 0; i < 9; i++)
    {
        cout << datb[i];
    }

    infile.close();

    return 0;
}

在磁碟檔案中有一個檔案指標,用來指明當前讀寫的位置。每次寫入或者讀出一個位元組,指標就向後移動一個位元組。對於二進位制檔案,允許對指標進行控制,使它移動到所需的位置,以便在該位置上進行讀寫。

  • ostream& seekp (streampos pos); 將輸出檔案中指標移動到指定的位置
  • ostream& seekp (streamoff off, ios_base::seekdir way); 以參照位置為基準對輸出檔案中的指標移動若干位元組
  • streampos tellp(); 返回輸出檔案指標當前的位置

  • istream& seekg (streampos pos); 將輸入檔案中指標移動到指定的位置
  • istream& seekg (streamoff off, ios_base::seekdir way); 以參照位置為基準對輸入檔案中的指標移動若干位元組
  • streampos tellg(); 返回輸入檔案指標當前的位置

其中,參照位置有以下幾個選擇:

  • ios_base::beg 檔案開始位置
  • ios_base::cur 檔案當前位置
  • ios_base::end 檔案末尾位置

獲取更多精彩,請關注「seniusen」!