首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> 關於C語言

Windows 文件操作全攻略:CRT 函數 vs C++ fstream 深度對比

     編輯:關於C語言

引言:文件操作的三種主流範式

在 Windows 平台進行 C/C++ 開發時,文件 I/O 是繞不開的基本功。本文系統梳理三種主流方式:**C 運行時庫 (CRT) `fxxx` 函數族**、**標準 C++ `fstream` 流庫**、以及 **Windows API (`CreateFile`/`ReadFile`/`WriteFile`)****,並重點對比前兩者的用法、性能與適用場景,助你根據項目需求做出最優技術選型。

一、 CRT `fxxx` 函數:經典、跨平台、輕量級

定義於 ``,是 C 語言標準規定的文件操作接口,具有極佳的跨平台性。

核心函數速查表

函數功能典型用途
`fopen`打開文件,返回 `FILE*`指定模式:`r`/`w`/`a`/`r+`/`w+`/`a+`,二進制加 `b`,編碼加 `ccs=ENCODING`
`fread`/`fwrite`二進制塊讀寫高性能數據傳輸,參數:緩衝區、元素大小、個數、流指針
`fgetc`/`fgets`/`fputc`/`fputs`文本行/字符讀寫處理文本文件,自動處理換行符轉換
`fclose`關閉流,刷新緩衝區必須調用,否則數據丟失
`feof`/`ferror`狀態檢查區分文件結束與讀取錯誤

`fopen` 模式組合詳解

// 典型組合示例
FILE *fp = fopen("data.bin", "rb+");      // 二進制讀寫,文件必須存在
FILE *fp = fopen("log.txt", "a+, ccs=UTF-8"); // 追加讀寫,指定 UTF-8 編碼 (需 VS2013+)

注意:`fread`/`fwrite` 返回實際讀寫元素個數,而非字節數;二進制模式下無換行符轉換,跨平台數據交換必須加 `b` 标志。

標準操作流程

  1. `fopen` 獲取 `FILE*` 句柄
  2. 循環調用 `fread`/`fwrite` 或格式化 I/O (`fprintf`/`fscanf`)
  3. `fclose` 釋放資源,**檢查返回值** 確保刷新成功

二、 C++ `fstream`:面向對象、類型安全、RAII 友好

定義於 ``,核心類繼承體系:`ios_base` → `ios` → `istream`/`ostream` → `ifstream`/`ofstream`/`fstream`。

三大核心類職責

  • `ifstream`:僅輸入 (讀),構造函數默認 `ios::in`
  • `ofstream`:僅輸出 (寫),默認 `ios::out | ios::trunc`
  • `fstream`:雙向讀寫,默認 `ios::in | ios::out`

流操作符 `<<` / `>>` 的優勢

ofstream out("config.txt");
out << "server_ip=" << "192.168.1.1" << '\n'
    << "port=" << 8080 << std::endl; // 類型安全,自動格式化

ifstream in("config.txt");
string key; int val;
while (in >> key >> val) { /* 解析 */ } // 自動跳過空白字符

`openmode` 标志位組合 (位或運算 `|`)

标志含義備註
`ios::in` / `ios::out`讀/寫模式基礎標志
`ios::binary`二進制模式禁用 `\n`↔`\r\n` 轉換,**跨平台二進制必加**
`ios::app`追加寫入隱含 `ios::out`,每次寫入前定位到末尾
`ios::ate`打開後定位末尾不隱含輸出模式,可配合讀模式查看文件大小
`ios::trunc`截斷文件`ofstream` 默認行為

RAII 與異常安全

void WriteLog(const string& msg) {
    ofstream log("app.log", ios::app | ios::binary); // 構造即打開
    if (!log) throw runtime_error("Open log failed");
    log << msg << '\n';
    // 析構自動 close() 並刷新,即使拋出異常也安全
}

三、 進階話題:編碼、性能與選型建議

1. 文件編碼處理

  • CRT:`fopen(..., "r, ccs=UTF-8")` 讓運行時自動轉換為內部宽字符 (需 `fgetwc` 等宽字符函數配合)。
  • C++17 `std::filesystem` + `std::codecvt` (已棄用):現代項目建議**統一使用 UTF-8**,讀入 `std::string` 後由第三方庫 (如 `iconv`, `ICU`, `simdutf`) 按需轉碼,避免標準庫 locale 的坑。

2. 性能對比實測建議

  • 大文件順序讀寫:CRT `fread`/`fwrite` 或 Windows API `ReadFile`/`WriteFile` (配合 `FILE_FLAG_SEQUENTIAL_SCAN` + 異步 I/O) 最快,`fstream` 因格式化解析與虛函數調用有 10%-30% 開銷。
  • 小文件文本解析:`fstream` 代碼可讀性、維護性優勢壓倒性能劣勢。
  • 內存映射文件:超大文件隨機訪問首選 `CreateFileMapping` + `MapViewOfFile`,OS 管理頁面換入換出。

3. 選型決策樹


需要跨平台 (Linux/macOS/Windows)?
  ├─ 是 → 嚴格要求 C 接口? → CRT (stdio.h)
  │         └─ 否 → 現代 C++ 項目? → fstream / 
  └─ 否 (純 Windows) →
        ├─ 高性能/異步/設備 I/O → Windows API (CreateFile, ReadFile, WriteFile, OVERLAPPED)
        ├─ 系統級/驅動開發 → Windows API / Kernel API (ZwCreateFile)
        └─ 應用層業務邏輯 → fstream (推薦) 或 CRT

四、 避坑指南:十大常見錯誤

  1. 忘記檢查 `fopen`/`open` 返回值:永遠假設打開會失敗。
  2. 文本模式下二進制數據損壞:`0x0A` 被誤轉換為 `0x0D 0x0A`,**二進制必加 `b`/`binary`**。
  3. `feof` 控制循環:`while(!feof(fp))` 會多讀一次,**正確寫法:`while(fread(...) == count)` 或 `while(stream >> var)`**。
  4. 緩衝區未刷新導致數據丟失:異常退出、進程崩潰時,調用 `fflush`/`flush` 或設置 `_IONBF` 無緩衝。
  5. 跨模塊傳遞 `FILE*` / `fstream`:不同 CRT 版本 (Debug/Release, VS2017/VS2022) 內部結構不兼容,**嚴禁跨 DLL 邊界傳遞**,應封裝純 C 接口或抽象介面。
  6. 大文件 `int` 截斷:使用 `fseek`/`ftell` (32位) 處理 >2GB 文件,**必須用 `fseeko64`/`ftello64` 或 `_fseeki64`/`_ftelli64`**。
  7. `fstream` 異常屏蔽:默認不拋出異常,需 `stream.exceptions(ios::failbit | ios::badbit)` 啟用。
  8. 宽字符文件讀寫混亂:`wfstream` 依賴 locale 和 `codecvt`,行為不可移植,**建議內部統一 UTF-8 `string`,邊界處轉碼**。
  9. 臨時文件未清理:使用 `tmpfile()` (CRT) 或 `std::filesystem::temp_directory_path()` + 隨機名 + `atexit` 註冊刪除。

五、 總結

**CRT** 是跨平台基石,適合 C 項目、嵌入式、極致性能場景;**`fstream`** 是現代 C++ 應用層首選,類型安全、RAII 自動管理、代碼清晰;**Windows API** 則是系統級、高性能、異步 I/O 的終極武器。實際工程中,**分層封裝** 是關鍵:底層用平台 API 實現高性能 I/O 引擎,上層暴露 C++ 流式介面供業務邏輯使用,兼得性能與開發效率。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved