HOWTO · C++

如何在 C++ 中寫入檔案

本文示範如何在 C++ 中寫入檔案。了解檔案串流和不同的檔案模式,並透過清晰的範例有效地管理 C++ 程式中的檔案操作。無論您需要附加資料還是覆寫現有內容,本指南都能幫到您。

本頁內容

一般文字輸出可使用 std::ofstream 搭配插入運算子(<<)寫入檔案。使用 std::ios::app 可將資料附加在檔案末尾;若要取代現有內容,則可使用 std::ios::trunc。若已知要寫入的字元或位元組數量,可使用串流的 write 成員函式;對應的 C 語言風格替代方案是 fwrite。

認識 C++ 檔案串流

檔案串流可讓 C++ 程式連接到指定名稱的檔案。加入 <fstream>,並建立 std::ofstream 來輸出;若同一個串流需要同時讀取與寫入,則使用 std::fstream。插入運算子適合輸出格式化文字,而字串中的換行字元會在檔案中開始新的一行。開啟檔案及寫入後都要檢查串流狀態,因為路徑看似有效,檔案仍可能無法存取,或儲存空間可能無法使用。

執行以下範例:

#include <fstream>
#include <iostream>

int main() {
    std::ofstream file("example.txt");
    if (!file) {
        std::cerr << "Could not open example.txt\n";
        return 1;
    }

    file << "Hello, C++!\n";
    file << "File output is ready.\n";
    file.close();
    if (!file) {
        std::cerr << "Could not finish writing example.txt\n";
        return 1;
    }

    std::cout << "Wrote example.txt\n";
    return 0;
}
Wrote example.txt

訊息會輸出到主控台;透過 file 寫入的兩行內容則會儲存在 example.txt。請用文字編輯器開啟該檔案,檢查其中內容。父目錄存在時,預設的純輸出 std::ofstream 會建立檔案;開啟時通常也會清除檔案原有內容。

以不同模式寫入檔案

開啟模式會決定如何處理檔案中的既有資料。std::ios::app 會讓每次寫入都從檔案末尾開始,適合用於需要累積內容的日誌或記錄。若檔案不存在,只要路徑與權限允許,以輸出模式開啟時就能建立檔案。以下獨立範例會以附加模式寫入一行內容,不需要事先建立 example.txt。

#include <fstream>
#include <iostream>

int main() {
    std::ofstream file("example.txt", std::ios::app);
    if (!file) {
        std::cerr << "Could not open example.txt\n";
        return 1;
    }

    file << "A new log entry\n";
    file.close();
    if (!file) {
        std::cerr << "Could not finish writing example.txt\n";
        return 1;
    }
    return 0;
}

提供的執行案例已成功完成,且沒有輸出主控台訊息。程式會寫入 A new log entry,並在後面加上換行;如果檔案已有內容,新的一行會接在原有內容後方。

使用 ios::trunc 寫入並覆寫檔案

開啟檔案時,std::ios::trunc 會捨棄檔案的原有內容。以輸出模式開啟 std::ofstream,且未使用 std::ios::app 時,通常也會有相同的截斷效果;若要明確表達這個選擇,可指定 std::ios::trunc。如果必須保留舊資料,請勿使用這兩種方式。

#include <fstream>
#include <iostream>

int main() {
    std::ofstream file("example.txt", std::ios::trunc);
    if (!file) {
        std::cerr << "Could not open example.txt\n";
        return 1;
    }

    file << "Current report\n";
    file.close();
    if (!file) {
        std::cerr << "Could not finish writing example.txt\n";
        return 1;
    }
    return 0;
}

提供的執行案例已成功完成,且沒有輸出主控台訊息。程式只會將 Current report 和一個換行寫入 example.txt,取代檔案原有的所有內容。

使用 fstream::write 寫入檔案

若資料已放在字元緩衝區中,而且長度已知,可使用串流成員函式 write。它接受字元指標及要寫入的字元數量;不會自動加入終止字元或換行。這個數量的型別是串流大小,因此傳入 data.size() 會寫入字串中儲存的字元,不包含結尾的 null 字元。呼叫後請檢查串流狀態,以偵測寫入是否失敗。如需了解介面,請參閱 cppreference 的 basic_ostream::write 文件。

#include <fstream>
#include <iostream>
#include <string>

int main() {
    const std::string data = "C++ buffer data\n";
    std::fstream file("buffer.txt", std::ios::out | std::ios::trunc);
    if (!file) {
        std::cerr << "Could not open buffer.txt\n";
        return 1;
    }

    file.write(data.data(), static_cast<std::streamsize>(data.size()));
    if (!file) {
        std::cerr << "Could not write buffer.txt\n";
        return 1;
    }
    file.close();
    if (!file) {
        std::cerr << "Could not finish writing buffer.txt\n";
        return 1;
    }
    return 0;
}

提供的執行案例已成功完成,且沒有輸出主控台訊息。程式會將 data 中的字元寫入 buffer.txt;換行字元包含在字串內,但不會寫入結尾的 null 字元。

字元寫入與位元組寫入的區別在於資料是否會被格式化。插入運算子會把值轉換成文字,而 write 和 fwrite 則不加格式化地複製緩衝區中的位元組。但僅複製位元組並不能保證它們原封不動地寫入檔案:在文字模式下開啟的串流,在行尾格式不同的平台上仍可能轉換換行符,例如 Windows 在輸出時會把 \n 轉換為 \r\n。當確切的位元組很重要時,請以二進位模式開啟串流,例如使用 std::ios::out | std::ios::binary | std::ios::trunc,或向 fopen 傳入 "wb" 而不是 "w",這樣就不會發生任何轉換。

使用 C 語言的 fwrite 函式寫入檔案

若使用 C 語言風格的檔案 I/O,fwrite 會接受指標、元素大小、元素數量及 FILE*。它的回傳值是已寫入的元素數量;只有在每個元素大小為一時,這個數量才等於位元組數。請檢查回傳的數量,再關閉檔案並檢查 fclose 的結果,因為緩衝寫入期間發生的錯誤可能會在關閉時才出現。

#include <cstdio>
#include <cstring>
#include <iostream>

int main() {
    const char data[] = "C file output\n";
    const std::size_t size = std::strlen(data);
    std::FILE* file = std::fopen("c-output.txt", "w");
    if (file == nullptr) {
        std::cerr << "Could not open c-output.txt\n";
        return 1;
    }

    const std::size_t written = std::fwrite(data, 1, size, file);
    const int close_status = std::fclose(file);
    if (written != size || close_status != 0) {
        std::cerr << "Could not finish writing c-output.txt\n";
        return 1;
    }
    return 0;
}

提供的執行案例已成功完成,且沒有輸出主控台訊息。程式會將 data 中的字元(包含換行)寫入 c-output.txt;"w" 模式會建立檔案,或截斷檔案原有內容。

結論

一般文字輸出請使用 std::ofstream 和 <<。若要保留舊內容並從檔案末尾新增資料,請選擇 std::ios::app;若要取代舊內容,請選擇 std::ios::trunc。已知緩衝區長度時可使用 write;使用 C 語言風格的 I/O 時則可使用 fwrite。請檢查檔案是否成功開啟及寫入;如果程式需要確認資料已完整寫入,也要檢查關閉時是否發生錯誤。

常見問題

  1. 如何確認檔案是否成功開啟? 直接檢查串流(if (!file)),或在開啟後立即呼叫 is_open()。若開啟失敗,請先處理錯誤,再進行寫入。

  2. 如何附加內容而不取代檔案原有內容? 使用 std::ios::app 開啟輸出串流。每次寫入都會指向檔案末尾。

  3. C++ 會建立不存在的檔案嗎? 若父目錄存在且程式具有相應權限,輸出串流和使用 "w" 模式的 fopen 都能建立檔案。父目錄不存在或權限不足時,開啟會失敗。

  4. 如何寫入多行內容? 使用 << 插入換行字元,或在傳給 write 的緩衝區中包含換行字元。write 和 fwrite 都不會自動加入換行。

  5. 需要關閉檔案嗎? 檔案串流在物件銷毀時會自動關閉;明確呼叫 close() 則能清楚標示操作完成的時點。若需要確認資料已寫入,請在關閉後檢查串流狀態。使用 C 的 FILE* 時,請呼叫 fclose 並檢查其回傳值。