程序首次運行時提示「首次運行」,否則讀取上次運行的時間並顯示;隨後將當前時間寫入文件,供下次讀取。核心涉及 time_t 與 struct tm 的二進制序列化/反序列化。
// 原代碼關鍵片段
void get_data(void) {
time_t t;
if ((fp = fopen(data_file, "r")) == NULL) // 錯誤1:文本模式打開二進制文件
printf("首次運行!\n");
else {
fread(&t, sizeof(time_t), 1, fp); // 讀取時間戳
struct tm *local;
time(&t); // 錯誤2:覆蓋剛讀取的歷史時間!
local = localtime(&t);
printf(...);
fclose(fp);
}
}
void put_data(void) {
time_t t;
if ((fp = fopne(data_file)) == NULL) // 錯誤3:拼寫錯誤 fopne → fopen
printf("文件打開失敗\n");
else {
fwrite(&t, sizeof(time_t), 1, fp); // 錯誤4:t 未初始化,寫入垃圾值
fclose(fp);
}
}
| 編號 | 錯誤類型 | 代碼位置 | 後果 | 修正方案 |
|---|---|---|---|---|
| 1 | 文件模式錯誤 | fopen(..., "r") | Windows下換行符轉換導致二進制數據損壞 | 使用 "rb" / "wb" 模式 |
| 2 | 邏輯倒置 | 讀取後調用 time(&t) | 歷史時間被當前時間覆蓋,失去持久化意義 | 直接用讀取的 t 轉換 localtime |
| 3 | 函數拼寫 | fopne | 編譯報錯/鏈接失敗 | 修正為 fopen |
| 4 | 未初始化變量 | fwrite(&t, ...) | 寫入棧垃圾數據,下次讀取時間亂碼 | 寫入前調用 time(&t) 獲取當前時間 |
#include <stdio.h>
#include <time.h>
#include <stdlib.h>
#define DATA_FILE "datetime.dat"
void get_data(void) {
FILE *fp = fopen(DATA_FILE, "rb");
if (fp == NULL) {
printf("程序首次運行!\n");
return;
}
time_t last_time;
if (fread(&last_time, sizeof(time_t), 1, fp) != 1) {
printf("讀取時間數據失敗!\n");
} else {
struct tm *local = localtime(&last_time);
printf("上次運行時間:%04d-%02d-%02d %02d:%02d:%02d\n",
local->tm_year + 1900, local->tm_mon + 1, local->tm_mday,
local->tm_hour, local->tm_min, local->tm_sec);
}
fclose(fp);
}
void put_data(void) {
FILE *fp = fopen(DATA_FILE, "wb");
if (fp == NULL) {
fprintf(stderr, "無法創建數據文件!\n");
return;
}
time_t now;
time(&now); // 先獲取當前時間
fwrite(&now, sizeof(time_t), 1, fp); // 再寫入
fclose(fp);
}
int main(void) {
get_data(); // 讀取並顯示歷史時間
put_data(); // 更新為當前時間
return 0;
}
time_t 大小/編碼(可能是 32/64 位整數或浮點數)。跨平台交換建議序列化為 ISO 8601 字符串或定長結構體(年/月/日/時/分/秒)。fread/fwrite 返回值,處理文件截斷、權限不足等異常。flock/LockFileEx)防止數據競爭。本例暴露了初學者常見的四類錯誤:文件模式誤用、邏輯順序顛倒、拼寫疏忽、未初始化讀寫。二進制文件操作核心原則:模式匹配(rb/wb)、讀寫對稱、變量初始化、錯誤檢查。掌握這些,才能寫出可靠的持久化代碼。