文件操作是 C 語言持久化存儲的核心能力。本文系統梳理 ANSI C 標準 I/O(FILE* 流)與 POSIX 低級 I/O(文件描述符)兩大體系,涵蓋打開模式、讀寫定位、錯誤處理、二進制/文本模式差異、緩衝機制及工程級最佳實踐。
FILE *fopen(const char *path, const char *mode);
| 模式 | 含義 | 文件需存在 | 截斷 | 可讀 | 可寫 | 追加 | 二進制 |
|---|---|---|---|---|---|---|---|
| r | 只讀 | 是 | 否 | ✓ | ✗ | ✗ | 文本 |
| w | 寫入 | 否 | 是 | ✗ | ✓ | ✗ | 文本 |
| a | 追加 | 否 | 否 | ✗ | ✓ | ✓ | 文本 |
| r+ | 讀寫 | 是 | 否 | ✓ | ✓ | ✗ | 文本 |
| w+ | 讀寫建新 | 否 | 是 | ✓ | ✓ | ✗ | 文本 |
| a+ | 讀寫追加 | 否 | 否 | ✓ | ✓ | ✓ | 文本 |
| rb/wb/ab/rb+/wb+/ab+ | 對應二進制模式 | - | - | - | - | - | 二進制 |
關鍵差異:Windows 文本模式下 \n ⇄ \r\n 轉換;二進制模式無轉換。跨平台處理二進制數據**必須用二進制模式**。
| 類別 | 函數 | 用途 | 返回值 |
|---|---|---|---|
| 字符 | fgetc/fputc | 單字符讀寫 | 字符/EOF |
| getc/putc | 宏版本,可能求值參數多次 | 同上 | |
| getchar/putchar | 標準輸入/輸出專用 | 同上 | |
| 行 | fgets/fputs | 讀取/寫入一行(含換行) | 指針/EOF |
| gets/puts | 已棄用/移除(緩衝區溢出風險) | - | |
| 格式化 | fprintf/fscanf | 格式化讀寫 | 字符數/EOF |
| vfprintf/vfscanf | va_list 版本 | 同上 | |
| 二進制塊 | fread/fwrite | 按大小/數量讀寫 | 成功項數 |
| feof/ferror/clearerr | 狀態檢查 | 非零/零 |
long ftell(FILE *stream); // 當前位置 int fseek(FILE *stream, long offset, int whence); // 絕對/相對定位 int fsetpos(FILE *stream, const fpos_t *pos); // 可移植定位(大文件) void rewind(FILE *stream); // 回到開頭 int fflush(FILE *stream); // 強制刷新輸出緩衝 void setbuf(FILE *stream, char *buf); // 全緩衝/無緩衝 int setvbuf(FILE *stream, char *buf, int mode, size_t size); // 精細控制
whence:SEEK_SET 開頭、SEEK_CUR 當前、SEEK_END 結尾。
FILE *fp = fopen("data.bin", "rb");
if (!fp) {
perror("fopen failed"); // 打印 errno 對應信息
return -1;
}
if (fread(buf, 1, sizeof(buf), fp) != sizeof(buf)) {
if (feof(fp)) puts("Unexpected EOF");
else if (ferror(fp)) perror("Read error");
}
fclose(fp); // 必須關閉,刷新緩衝、釋放資源
POSIX 系統調用接口,無用戶態緩衝,直接與內核交互。
int open(const char *path, int flags, mode_t mode); // mode 僅創建時有效 ssize_t read(int fd, void *buf, size_t count); ssize_t write(int fd, const void *buf, size_t count); off_t lseek(int fd, off_t offset, int whence); int close(int fd); int fsync(int fd); // 強制寫入磁盤 int ftruncate(int fd, off_t length); // 截斷/擴展文件
| Flag | 含義 |
|---|---|
| O_RDONLY / O_WRONLY / O_RDWR | 讀/寫/讀寫(必選一) |
| O_CREAT | 不存在則創建(需 mode) |
| O_TRUNC | 截斷至零長度 |
| O_APPEND | 追加模式(原子寫入尾部) |
| O_EXCL | 與 O_CREAT 同用,存在則報錯 |
| O_SYNC / O_DSYNC | 同步寫入(數據/元數據落盤) |
| O_NONBLOCK | 非阻塞模式 |
| O_CLOEXEC | exec 時自動關閉(安全) |
struct Record { int id; double val; char name[20]; };
// 直接 fwrite(&rec, sizeof(rec), 1, fp) 存在問題:
// - 填充字節不確定
// - 大小端不同機器不可讀
// - 指針字段無意義
// - 結構體版本演進不兼容
goto cleanup 或 __attribute__((cleanup)) 模擬-D_FILE_OFFSET_BITS=64,用 fseeko/ftello/off_trename() 覆蓋,防止寫入中斷損壞數據mkstemp()/tmpfile() 避免競爭條件open 時 mode=0600,umask 配合strerror(errno) 記錄上下文,便於事後分析C 語言文件操作雖接口樸素,但掌握緩衝機制、定位語義、錯誤處理、跨平台差異、序列化策略,才能構建健壯的存儲層。現代項目建議:業務邏輯用高級序列化庫,底層熱路徑用 mmap/IO_uring,標準 I/O 用於日誌/配置等非性能敏感場景。