順序讀寫僅能線性流動,隨機訪問則賦予程序「跳轉至任意位置讀寫」的能力,是數據庫索引、大文件處理、二進制格式解析的基石。本文系統講解 fseek/ftell 定位機制、fread/fwrite 數據塊傳輸,並給出二進制文件隨機讀寫完整範例。
fseek:移動文件位置指示器int fseek(FILE *stream, long offset, int fromwhere);
offset:相對 fromwhere 的位元組偏移量(long 支援 >64KB 文件)。fromwhere 三大基準點:| 符號常數 | 值 | 含義 |
|---|---|---|
SEEK_SET | 0 | 文件開頭 |
SEEK_CUR | 1 | 當前位置 |
SEEK_END | 2 | 文件末尾 |
ftell:獲取當前位置long ftell(FILE *stream);
long)。fseek 實現「記住位置 → 跳轉 → 恢復位置」。fread:讀取數據塊size_t fread(void *buf, size_t size, size_t count, FILE *stream);
stream 讀取 count 個項目,每項 size 位元組,存入 buf。count,遇 EOF/錯誤)。size * count 位元組。fwrite:寫入數據塊size_t fwrite(const void *buf, size_t size, size_t count, FILE *stream);
stream 寫入 buf 中 count 個項目,每項 size 位元組。#include
#include
main() {
FILE *fp;
if ((fp = fopen("test_b.dat", "rb")) == NULL) {
printf("Can't open file");
exit(1);
}
fseek(fp, 8, SEEK_SET); // 從開頭偏移 8 位元組
fgetc(fp); // 讀取該位置字符
fclose(fp);
}
#include
#include
struct Record {
int id;
char name[20];
double score;
};
int main() {
FILE *fp = fopen("records.dat", "wb+");
if (!fp) return 1;
struct Record recs[3] = {
{1, "Alice", 92.5},
{2, "Bob", 87.0},
{3, "Carol", 95.5}
};
/* 寫入 3 條記錄 */
fwrite(recs, sizeof(struct Record), 3, fp);
/* 隨機讀取第 2 條 (索引 1) */
fseek(fp, 1 * sizeof(struct Record), SEEK_SET);
struct Record r;
fread(&r, sizeof(struct Record), 1, fp);
printf("ID=%d Name=%s Score=%.1f\n", r.id, r.name, r.score);
/* 修改第 1 條並寫回 */
fseek(fp, 0, SEEK_SET);
r.score = 93.0;
fwrite(&r, sizeof(struct Record), 1, fp);
fclose(fp);
return 0;
}
| 陷阱 | 說明 | 對策 |
|---|---|---|
| 文本模式用 fseek | 換行符轉換導致偏移量不準 | **二進制模式** ("rb"/"wb") 使用隨機訪問 |
| fread/fwrite 返回值未檢查 | 部分讀寫/錯誤靜默失敗 | 必檢查返回值 == count |
| 結構體填充導致跨平台不兼容 | 不同編譯器對齊不同,文件格式不固定 | 使用 #pragma pack 或手動序列化 |
| offset 用 int 而非 long | 大文件 (>2GB) 溢出 | 堅持 long / off_t / int64_t |
| ftell 返回 -1 未處理 | 錯誤傳播 | 檢查 if (pos == -1L) handle_error(); |
{key, offset} 陣列。fseek → fread 讀取完整記錄。fseek/ftell 提供**位元組級定位**,fread/fwrite 提供**類型感知塊傳輸**,二者結合構成 C 語言隨機訪問的核心原語。牢記「二進制模式、檢查返回值、結構體對齊、64位偏移」四大鐵律,即可構建高效、可靠的文件存儲引擎。