標準 I/O 分層架構
應用程式
│
▼
┌─────────────────────┐
│ 標準 I/O 庫 (stdio.h) │ ← 緩衝、格式化、流抽象
│ FILE*, fopen, fclose │
│ fread, fwrite, fprintf│
│ fscanf, fgets, fputs │
└─────────┬───────────┘
│ 函數調用
▼
┌─────────────────────┐
│ 系統調用接口 (unistd.h) │ ← 無緩衝、文件描述符、內核態
│ open, close, read, write│
│ lseek, stat, ioctl │
└─────────┬───────────┘
│ 系統調用
▼
┌─────────────────────┐
│ 內核 VFS 層 │ ← 文件系統抽象、頁面緩存、權限檢查
└─────────┬───────────┘
▼
具體文件系統 / 設備驅動
核心函數分類速查表
1. 文件打開與關閉
| 函數 | 用途 | 關鍵參數 |
fopen(path, mode) | 打開文件返回 FILE* | mode: r/w/a/r+/w+/a+ + b(二進制) |
freopen(path, mode, fp) | 重新關聯流 (常用於重定向) | |
fclose(fp) | 刷新緩衝區、關閉文件、釋放 FILE* | 返回 0 成功, EOF 失敗 |
fdopen(fd, mode) | 文件描述符轉 FILE* | 整合底層與標準 I/O |
fileno(fp) | FILE* 取文件描述符 | 用於系統調用互操作 |
2. 格式化輸入輸出
- 輸出:
fprintf(fp, fmt, ...), printf(fmt, ...) (stdout), sprintf(buf, fmt, ...), snprintf(buf, n, fmt, ...) (安全版)
- 輸入:
fscanf(fp, fmt, ...), scanf(fmt, ...) (stdin), sscanf(str, fmt, ...)
- 格式符核心:
%[flags][width][.prec][len]type (d/i/u/o/x/X/f/e/E/g/G/c/s/p/n/%...)
3. 字元/行/塊 輸入輸出
| 層級 | 輸入 | 輸出 | 特點 |
| 字元 | fgetc(fp), getc(fp), getchar() | fputc(c, fp), putc(c, fp), putchar(c) | 單字元、宏/函數雙形態 |
| 行 | fgets(buf, n, fp) | fputs(str, fp), puts(str) (stdout+換行) | 保留換行符、緩衝高效 |
| 塊 (二進制) | fread(ptr, size, nmemb, fp) | fwrite(ptr, size, nmemb, fp) | 返回完成項數、適合結構體/陣列 |
4. 文件定位與狀態查詢
fseek(fp, offset, whence) / ftell(fp) (long, 32 位限制)
fseeko(fp, offset, whence) / ftello(fp) (off_t, 64 位大文件)
rewind(fp) (等同 fseek(fp,0,SEEK_SET) + 清錯誤標誌)
fgetpos(fp, pos) / fsetpos(fp, pos) (fpos_t, 可移植大文件)
feof(fp), ferror(fp), clearerr(fp), fileno(fp)
5. 緩衝區控制
setbuf(fp, buf):指定緩衝區 (NULL=無緩衝, BUFSIZ=全緩衝)
setvbuf(fp, buf, mode, size):mode=_IOFBF(全)/_IOLBF(行)/_IONBF(無)
fflush(fp):強制刷新輸出緩衝區
setbuffer/setlinebuf (BSD 擴展)
6. 臨時文件與標準流
tmpfile():創建臨時二進制文件 (關閉自動刪除)
tmpnam(buf):生成唯一臨時文件名
- 標準流:
stdin, stdout, stderr (預設行緩衝/無緩衝)
錯誤處理模式
FILE *fp = fopen("data.bin", "rb");
if (!fp) {
perror("fopen failed"); // 輸出 "fopen failed: No such file or directory"
return -1;
}
if (fread(buf, 1, sizeof(buf), fp) != sizeof(buf)) {
if (feof(fp)) fprintf(stderr, "Unexpected EOF\n");
else if (ferror(fp)) perror("fread error");
}
fclose(fp);
最佳實踐清單
- 二進制文件必加 'b':
"rb", "wb" 避免 Windows 換行符轉換破壞數據
- 輸入緩衝區防溢出:用
fgets/snprintf/scanf("%99s", buf) 限制長度
- 大文件用 fseeko/ftello/fgetpos:支援 >2GB 文件
- 關鍵數據落盤必 fflush/fsync:
fflush(fp); fsync(fileno(fp)) (POSIX)
- 資源獲取即初始化 (RAII 風格):打開即檢查,結構體封裝 FILE* 確保釋放
- 線程安全:標準 I/O 函數內部含鎖,但邏輯層面仍需注意交錯調用
總結
C 標準 I/O 庫以 FILE* 流抽象 統一了設備、文件、管道、套接字等異構資源,提供分級緩衝、豐富格式化、可移植接口。掌握「流、緩衝、格式、定位、錯誤」五大核心維度,能應對 90% 以上的文件操作場景。