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

C語言文件讀寫全解析:文本模式 vs 二進制模式、fread/fwrite 實戰指南

     編輯:關於C語言

從文本到二進制:C 語言文件 I/O 兩大范式的選擇與實踐

C 語言文件操作分為 **文本流** 與 **二進制流** 兩種模式。文本流適合人類可讀配置/日志,二進制流適合高性能數據持久化、結構體序列化、跨平台數據交換。本文深度對比兩模式差異,重點講解 `fread`/`fwrite` 二進制讀寫核心 API、內存佈局映射原理、常見坑點與最佳實踐。

一、兩大模式本質區別

二、二進制讀寫核心 API:`fwrite` / `fread`

size_t fwrite(const void *buffer, size_t size, size_t count, FILE *stream);
size_t fread(void *buffer, size_t size, size_t count, FILE *stream);

參數解析:

  • `buffer`:數據緩衝區地址 (任意類型指針 `void*`)
  • `size`:**單個元素大小** (字節),通常用 `sizeof(Type)`
  • `count`:元素個數
  • `stream`:`fopen` 返回的 `FILE*` (必須帶 `b` 模式)
  • **返回值**:**成功讀寫的元素個數** (非字節數)。返回值 `< count` 表示錯誤或 EOF。

經典用法:結構體數組整體寫入/讀取

#include 
#include 

typedef struct {
    int id;
    char name[32];
    float score;
} Student;

int main() {
    Student class[3] = {
        {1, "Alice", 92.5f},
        {2, "Bob",   87.0f},
        {3, "Charlie", 95.5f}
    };
    FILE *fp = fopen("students.dat", "wb");
    if (!fp) return 1;

    // 寫入:一次性寫入 3 個 Student 結構體
    size_t written = fwrite(class, sizeof(Student), 3, fp);
    printf("Written %zu records\n", written);  // 3
    fclose(fp);

    // 讀取:還原到新數組
    Student read_class[3];
    fp = fopen("students.dat", "rb");
    size_t read = fread(read_class, sizeof(Student), 3, fp);
    printf("Read %zu records\n", read);  // 3
    for (int i = 0; i < read; i++)
        printf("%d: %s %.1f\n", read_class[i].id, read_class[i].name, read_class[i].score);
    fclose(fp);
    return 0;
}

三、內存佈局可視化:為什麼二進制「快」且「隱私」

假設 `int a = 64` (佔 2 字節,小端序):

特性文本模式 (`"r"`, `"w"`, `"a"`)二進制模式 (`"rb"`, `"wb"`, `"ab"`)
數據表示字符序列 (ASCII/UTF-8 編碼)原始位模式 (內存位映像)
換行符處理**平台相關轉換**:Windows `\r\n` ⇄ `\n`,Linux/Unix 無轉換**無任何轉換**,原樣讀寫
EOF 標記Windows 可能識別 `Ctrl+Z` (0x1A) 為 EOF純粹按字節計數/文件末尾
適用場景配置文件、日志、CSV、源代碼、人類閱讀圖像/音視頻、結構體數組、數據庫頁、網絡包存檔
可移植性跨平台文本文件需注意換行符**完全可移植** (同端序前提下)

核心結論:

  • **二進制 = 內存位映像直接落盤**,CPU 免去「數值 ⇄ 字符」解析開銷,**速度快、體積小、精度無損**。
  • **非加密**:二進制僅是「不直接可讀」,`char` 字符串字段仍可被文本編輯器識別。**真正加密需 AES/ChaCha20 等算法**。
  • **端序陷阱**:跨平台讀寫時,需約定網絡序 (大端) 或統一小端,或在文件頭寫入 Magic Number 標識端序。

四、進階實戰技巧

1. 結構體對齊與 `#pragma pack`

// 默認對齊可能導致結構體大小跨平台不一致
#pragma pack(push, 1)  // 1 字節對齊,消除填充
typedef struct {
    char type;      // 1
    int  len;       // 4
    short checksum; // 2
} PacketHeader;     // 大小 = 7 字節 (無填充)
#pragma pack(pop)

// 寫入時確保雙方協議一致,或顯式序列化各字段 (網絡序 htonl/ntohl)

2. 大文件分塊讀寫 (避免大緩衝區)


#define CHUNK_SIZE (64 * 1024)
char buf[CHUNK_SIZE];
while ((n = fread(buf, 1, CHUNK_SIZE, src)) > 0)
    fwrite(buf, 1, n, dst);

3. 錯誤處理模板


if (fwrite(data, sizeof(Item), count, fp) != count) {
    if (ferror(fp)) perror("Write error");
    else if (feof(fp)) fprintf(stderr, "Unexpected EOF\n");
    // 處理錯誤
}

4. 隨機訪問:`fseek` / `ftell` / `fgetpos` / `fsetpos`


// 讀取第 i 個記錄
fseek(fp, i * sizeof(Record), SEEK_SET);
fread(&rec, sizeof(Record), 1, fp);

// 獲取文件大小
fseek(fp, 0, SEEK_END);
long size = ftell(fp);
fseek(fp, 0, SEEK_SET);

五、文本 vs 二進制選擇決策表

存儲方式內存/文件位模式 (十六進制)文本編輯器顯示說明
二進制 `fwrite(&a, 2, 1, fp)``40 00` (小端: 低位在前)亂碼:`@` + NUL (0x40='@', 0x00=NUL)直接寫入內存位模式,非文本
文本 `fprintf(fp, "%d", a)``36 34` ('6'=0x36, '4'=0x34)可讀:`64`轉換為字符編碼,佔用更多空間

六、總結

  1. 二進制模式 (`"rb"/"wb"`) 是高性能、精確、可移植數據持久化的基石。
  2. `fwrite`/`fread` 以「元素大小 × 個數」為單位操作內存塊,**返回元素數而非字節數**,檢查返回值是必修課。
  3. **結構體直接讀寫** 便利但隱含對齊、端序、版本兼容風險,**嚴肅項目建議顯式序列化 (Protobuf/FlatBuffers/手寫)**。
  4. 二進制文件**不是加密**,敏感數據必須應用層加密。
  5. 文本模式處理換行轉換,二進制模式**絕不轉換**,跨平台文本文件建議統一 LF (`\n`)。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved
需求推薦模式關鍵 API
人類需閱讀/編輯配置文本`fprintf`/`fscanf`/`fgets`/`fputs`
高性能海量數據存儲二進制`fwrite`/`fread` + 結構體
跨平台數據交換二進制 + 協議規範`fwrite`/`fread` + `htonl`/`ntohl`
日志/文本處理管道文本`fprintf`/`fgets`
嵌入式固件/配置區二進制`fwrite`/`fread` + CRC 校驗