文字檔 vs 二進位檔
"w" / "r":文字模式。Windows 下 \n ⇄ \r\n 轉換,可能改變位元組內容。
"wb" / "rb":二進位模式。**不做任何轉換**,位元組完全對應磁碟內容,跨平台相容。
核心函式
FILE *fp = fopen("data.bin", "wb"); // 寫入二進位
if (!fp) return -1;
int arr[10] = {1,2,3,4,5,6,7,8,9,10};
// 寫入整個陣列
fwrite(arr, sizeof(int), 10, fp); // 回傳實際寫入項目數
fclose(fp);
// 讀取
fp = fopen("data.bin", "rb");
int buf[10];
size_t n = fread(buf, sizeof(int), 10, fp); // n == 10 表示讀滿
fclose(fp);
關鍵細節
- 結構體寫入:
fwrite(&st, sizeof(Struct), 1, fp);。陷阱:結構體有填充位元組、指標成員會儲存無效位址。跨平台/長期儲存請序列化(逐欄位寫入或用 Protocol Buffers/MessagePack)。
- 位元組序:x86 為 Little-endian,網路傳輸/跨架構需轉大端(
htonl/htons 或手動移位)。
- 錯誤檢查:永遠檢查
fopen、fread/fwrite 回傳值,配合 ferror/feof 區分錯誤與檔案結尾。
- 定位:
fseek(fp, offset, SEEK_SET)、ftell(fp)、rewind(fp) 實現隨機存取。
完整範例:儲存學生紀錄(定長結構體)
#include
#include
typedef struct {
int id;
char name[20];
float score;
} Student;
int save_students(const char *filename, Student *stus, int n) {
FILE *fp = fopen(filename, "wb");
if (!fp) return -1;
int written = fwrite(stus, sizeof(Student), n, fp);
fclose(fp);
return written;
}
int load_students(const char *filename, Student *stus, int max_n) {
FILE *fp = fopen(filename, "rb");
if (!fp) return -1;
int read = fread(stus, sizeof(Student), max_n, fp);
fclose(fp);
return read;
}
避坑清單
- 忘記
"b" 導致 Windows 讀寫換行符損壞二進位資料。
- 直接寫入指標/動態記憶體位址,讀出後指向無效記憶體。
- 結構體對齊填充導致
sizeof 跨平台不一致,建議 #pragma pack(1) 或手動序列化。