C++ 中的文件和流是程序與外部數據交互的橋樑,它們提供了一套強大而靈活的標準庫工具,用於處理數據的持久化存儲與讀取。下面這張表格彙總了其核心組件和用途,幫你快速建立整體認知。

組件類別

核心類/頭文件

主要用途

文件流 (File Streams)

ifstream, ofstream, fstream(來自 <fstream>)

用於對磁盤文件進行讀取、寫入和讀寫操作。

字符串流 (String Streams)

istringstream, ostringstream, stringstream(來自 <sstream>)

用於像操作流一樣處理字符串,常用於數據格式轉換或拼接。

標準 I/O 對象

cin, cout, cerr, clog(來自 <iostream>)

用於處理標準輸入(如鍵盤)和標準輸出/錯誤(如控制枱)。

文件系統庫 (C++17)

std::filesystem::path等 (來自 <filesystem>)

提供跨平台的文件和目錄管理操作,如路徑處理、創建、刪除、遍歷。

🔑 核心文件流操作

在C++中進行文件I/O,主要依賴定義在 <fstream>頭文件中的三個類:

  • std::ifstream:輸入文件流,專門用於從文件讀取數據。
  • std::ofstream:輸出文件流,專門用於向文件寫入數據。
  • std::fstream:兼具輸入和輸出功能的文件流。

文件操作通常遵循以下標準步驟:

  1. 包含頭文件#include <fstream>
  2. 創建流對象並打開文件:可以在構造函數中直接指定文件名和模式,也可以先創建對象,再調用 open()方法。
  3. 檢查是否打開成功:使用 is_open()方法或直接判斷流對象的狀態(如 if (!file))是至關重要的好習慣。
  4. 進行讀寫操作
  5. 關閉文件:雖然流對象析構時會自動關閉文件,但顯式調用 close()可以及時釋放資源。

寫入文件

使用 ofstream寫入文本數據非常簡單,其用法和 cout類似:

#include <iostream>
#include <fstream>
int main() {
    std::ofstream outFile("example.txt"); // 創建並嘗試打開文件
    if (!outFile) { // 檢查文件是否成功打開
        std::cerr << "Failed to open file for writing!" << std::endl;
        return 1;
    }
    outFile << "Hello, World!" << std::endl;
    outFile << "This is a second line." << std::endl;
    outFile.close(); // 關閉文件
    std::cout << "Data written successfully." << std::endl;
    return 0;
}

讀取文件

使用 ifstream讀取文件有幾種常見方式:

  • 逐行讀取:使用 std::getline(),適合處理文本文件。
  • 逐詞讀取:使用 >>操作符,它會忽略空白字符。
  • 一次性讀取整個文件:結合 std::istreambuf_iterator,適用於已知文件不大的情況。
#include <iostream>
#include <fstream>
#include <string>
int main() {
    std::ifstream inFile("example.txt");
    if (!inFile.is_open()) {
        std::cerr << "Failed to open file for reading!" << std::endl;
        return 1;
    }
    std::string line;
    while (std::getline(inFile, line)) { // 逐行讀取
        std::cout << line << std::endl;
    }
    inFile.close();
    return 0;
}

⚙️ 掌握關鍵技術與模式

靈活運用文件操作,需要理解一些關鍵概念。

文件打開模式

在打開文件時,可以指定模式來控制其行為,模式通過位或操作符 |組合:

模式

描述

std::ios::in

為讀取打開文件。

std::ios::out

為寫入打開文件(默認會截斷已有文件)。

std::ios::app

追加模式,所有寫入都添加到文件尾部。

std::ios::binary

以二進制模式打開文件,避免某些轉換。

std::ios::trunc

如果文件已存在,則先清空其內容(ofstream默認)。

例如,以讀寫和追加模式打開文件:std::fstream file("data.txt", std::ios::in | std::ios::out | std::ios::app);

二進制文件操作

處理非文本數據(如圖片、結構體)時,需使用二進制模式。這會禁止換行符轉換等特殊處理,保證數據原樣讀寫。

  • 寫入:使用 write()成員函數,它接受一個 char*指針和要寫入的字節數。通常需要 reinterpret_cast進行類型轉換。
  • 讀取:使用對應的 read()成員函數。
#include <fstream>
struct Data {
    int id;
    double value;
};
int main() {
    Data data = {1, 3.14};
    // 寫入二進制文件
    std::ofstream out("data.bin", std::ios::binary);
    out.write(reinterpret_cast<char*>(&data), sizeof(data));
    out.close();
    // 讀取二進制文件
    Data readData;
    std::ifstream in("data.bin", std::ios::binary);
    in.read(reinterpret_cast<char*>(&readData), sizeof(readData));
    in.close();
    std::cout << "ID: " << readData.id << ", Value: " << readData.value << std::endl;
    return 0;
}

隨機訪問文件指針

C++允許在文件內任意位置進行讀寫,即隨機訪問。這是通過操作文件指針實現的:

  • seekg(offset, origin):移動輸入(讀)指針。origin可以是 std::ios::beg(文件頭)、std::ios::cur(當前位置)或 std::ios::end(文件尾)。
  • seekp(offset, origin):移動輸出(寫)指針。
  • tellg()/ tellp():返回當前讀/寫指針的位置。

例如,file.seekg(-10, std::ios::end);將讀指針移動到距離文件末尾10個字節的位置。

🛡️ 錯誤處理與狀態檢查

文件操作容易出錯,健壯的程序必須檢查操作結果。文件流對象提供了一系列方法來檢查其狀態:

  • good():流處於正常狀態,所有操作都可用。
  • eof():已到達文件末尾。注意,僅當嘗試讀取超過文件尾時,此標誌才被設置。
  • fail():上次操作失敗(如類型不匹配),但流尚未完全損壞。
  • bad():發生了嚴重錯誤,流可能無法再使用。

通常,在文件操作後檢查流狀態更可靠,例如 while (getline(inFile, line)) { ... }會在讀取失敗(包括到達文件尾)時自動退出循環。

🚀 進階主題與現代方法

字符串流

<sstream>頭文件提供了字符串流類(如 std::istringstream, std::ostringstream),它們允許將字符串當作流來處理,常用於數據格式轉換或拼接。

#include <sstream>
#include <iostream>
#include <string>
int main() {
    // 從字符串解析數據
    std::string input = "100 3.14 hello";
    std::istringstream iss(input);
    int i; float f; std::string s;
    iss >> i >> f >> s;
    // 使用字符串流拼接
    std::ostringstream oss;
    oss << "Parsed: " << i << ", " << f << ", " << s;
    std::cout << oss.str() << std::endl; // 輸出: Parsed: 100, 3.14, hello
    return 0;
}

C++17 文件系統庫

C++17 引入了 <filesystem>庫,它提供了一套現代化、跨平台的文件和目錄操作工具,大大簡化了路徑處理、目錄遍歷等任務。

#include <iostream>
#include <filesystem>
namespace fs = std::filesystem;
int main() {
    fs::path p = "example.txt";
    if (fs::exists(p)) {
        std::cout << "File size: " << fs::file_size(p) << " bytes\n";
    }
    // 遍歷目錄
    for (const auto& entry : fs::directory_iterator(".")) {
        std::cout << entry.path() << std::endl;
    }
    return 0;
}

💎 總結

C++的文件和流機制構成了一個強大而靈活的數據I/O生態系統。通過掌握基於 <fstream>的傳統文件操作、理解二進制處理和隨機訪問、善用 <sstream>進行字符串處理,並在支持C++17及以上標準的環境中積極運用現代化的 <filesystem>庫,你能夠高效地處理各種數據持久化需求。

希望這份介紹能幫助你深入理解並有效運用C++的文件和流。如果你對某個特定方面還有疑問,我們可以繼續深入探討。