檔案操作是 C 語言持久化資料的核心技能。本文系統講解標準 I/O 庫(stdio.h)的檔案指標模式:開啟模式對照表、錯誤處理、字元/字串/格式化/二進位讀寫、隨機存取(fseek/ftell)、緩衝區控制,並提供可編譯執行的完整範例程式。
fopen
FILE* fp = fopen("data.txt", "r"); // 失敗回傳 NULL
if (!fp) { perror("open failed"); exit(EXIT_FAILURE); }
| 模式 | 說明 | 檔案須存在 | 寫入位置 | 截斷 |
|---|---|---|---|---|
r | 唯讀 | 是 | 開頭 | 否 |
w | 唯寫 | 否(自動建立) | 開頭 | 是 |
a | 附加 | 否 | 結尾 | 否 |
r+ | 讀寫 | 是 | 開頭 | 否 |
w+ | 讀寫 | 否 | 開頭 | 是 |
a+ | 讀寫附加 | 否 | 結尾 | 否 |
rb/wb/ab/rb+/wb+/ab+ | 二進位模式對應 | 同上 | 同上 | 同上 |
Windows 下文字模式會將 \n ↔ \r\n 轉換,二進位模式不轉換,處理圖片/結構體必須加 b。
fclose / fcloseall
if (fclose(fp) != 0) { perror("close failed"); }
// fcloseall() 關閉所有開啟檔案,回傳關閉數量 (非標準,MSVC 支援)
⚠️ 未 fclose 導致緩衝區資料遺失、檔案損壞、檔案描述符洩漏(進程限制通常 512/1024)。
// 讀
int ch = fgetc(fp); // 回傳 unsigned char 轉 int,EOF(-1) 表示結束/錯誤
// 寫
fputc(ch, fp); // 成功回傳寫入字元,失敗回傳 EOF
// 等價巨集:getc/putc (可能為巨集,勿傳有副作用運算式)
while ((ch = fgetc(src)) != EOF) fputc(ch, dst);
// 讀一行 (含 \n,末尾加 \0),最多 n-1 字元
char buf[256];
fgets(buf, sizeof(buf), fp); // 遇 \n 或 EOF 停止,回傳 buf 或 NULL
// 寫字串 (不含 \0)
fputs(str, fp);
fprintf(fp, "%d %s %.2f", id, name, score); // 寫
fscanf(fp, "%d %s %f", &id, name, &score); // 讀,需傳址,回傳成功轉換項目數
fread / fwrite
struct Student { int id; char name[20]; float gpa; };
Student stu = {1, "Alice", 3.8};
fwrite(&stu, sizeof(Student), 1, fp); // 寫入結構體
fread(&stu, sizeof(Student), 1, fp); // 讀回
適用於結構體陣列、大量數值資料,速度遠快於格式化 I/O。
fseek / ftell / rewind
fseek(fp, 0, SEEK_END); // 移至檔尾
long size = ftell(fp); // 取得檔案大小
fseek(fp, 0, SEEK_SET); // 回到開頭
fseek(fp, -sizeof(Student), SEEK_END); // 讀取最後一筆記錄
| 起始點 | 常數 | 說明 |
|---|---|---|
| 檔案開頭 | SEEK_SET | offset ≥ 0 |
| 目前位置 | SEEK_CUR | offset 可正負 |
| 檔案結尾 | SEEK_END | offset ≤ 0 |
#include
#include
#include
#define FILENAME "students.dat"
#define MAX 100
typedef struct { int id; char name[20]; float score; } Student;
void Add() {
FILE* fp = fopen(FILENAME, "ab");
Student s; printf("ID Name Score: "); scanf("%d %19s %f", &s.id, s.name, &s.score);
fwrite(&s, sizeof(Student), 1, fp); fclose(fp);
}
void List() {
FILE* fp = fopen(FILENAME, "rb");
Student s; int i=1;
while (fread(&s, sizeof(Student), 1, fp) == 1)
printf("%2d: %4d %-10s %.1f\n", i++, s.id, s.name, s.score);
fclose(fp);
}
int main() {
int cmd;
while (1) {
printf("\n1.Add 2.List 0.Exit: "); scanf("%d", &cmd);
if (cmd==1) Add(); else if (cmd==2) List(); else break;
}
return 0;
}
feof(fp) 迴圈條件錯誤:應檢查讀取函式回傳值,而非 !feof(fp)(會多讀一次)。fflush(fp) 或 fclose(fp)。