fopen, fclose, freopen, fdopen, filenofprintf, fscanf, sprintf, sscanf, snprintffread, fwritefgetc, fputc, fgets, fputs, getc, putc, getchar, putcharfseek, ftell, rewind, fgetpos, fsetpos, feof, ferror, clearerrsetbuf, setvbuf, setbuffer, setlinebuf, fflushtmpfile, tmpnam, mktemp| 函數 | 原型 | 說明 | 關鍵點 |
|---|---|---|---|
fopen | FILE* fopen(const char* path, const char* mode) | 打開文件 | 模式:r/w/a/r+/w+/a+ + b(二進制)。失敗返回 NULL |
fclose | int fclose(FILE* stream) | 關閉文件 | 刷新緩衝區,釋放資源。成功 0,失敗 EOF |
fprintf | int fprintf(FILE* stream, const char* fmt, ...) | 格式化寫入 | 同 printf,輸出到文件 |
fscanf | int fscanf(FILE* stream, const char* fmt, ...) | 格式化讀取 | 同 scanf,遇空白/換行停止。返回匹配項數 |
fwrite | size_t fwrite(const void* ptr, size_t size, size_t nmemb, FILE* stream) | 二進制塊寫入 | 寫入 nmemb 個 size 字節項。返回實際寫入項數 |
fread | size_t fread(void* ptr, size_t size, size_t nmemb, FILE* stream) | 二進制塊讀取 | 返回實際讀取項數。需配合 feof/ferror 判斷原因 |
fseek | int fseek(FILE* stream, long offset, int whence) | 移動文件指針 | whence: SEEK_SET/CUR/END。成功 0,失敗 -1 |
ftell | long ftell(FILE* stream) | 獲取當前位置 | 返回距文件開頭字節數。失敗 -1 |
rewind | void rewind(FILE* stream) | 指針歸零 | 等價 fseek(stream,0,SEEK_SET),清除錯誤標志 |
feof | int feof(FILE* stream) | 判斷文件結束 | 非零=已到末尾。注意:讀操作失敗後才置位 |
ferror | int ferror(FILE* stream) | 判斷錯誤 | 非零=發生錯誤。需 clearerr 重置 |
fflush | int fflush(FILE* stream) | 刷新緩衝區 | 強制寫入磁盤。stream=NULL 刷新所有流 |
setvbuf | int setvbuf(FILE* stream, char* buf, int mode, size_t size) | 設置緩衝模式 | _IOFBF 全緩衝/_IOLBF 行緩衝/_IONBF 無緩衝。須在讀寫前調用 |
fgetc | int fgetc(FILE* stream) | 讀單字符 | 返回 unsigned char 轉 int。EOF=-1 表示末尾/錯誤 |
fputc | int fputc(int c, FILE* stream) | 寫單字符 | 寫入低 8 位。返回寫入字符,失敗 EOF |
fgets | char* fgets(char* s, int n, FILE* stream) | 讀一行 | 讀 n-1 字符或遇換行。保留 \n。末尾補 \0 |
fputs | int fputs(const char* s, FILE* stream) | 寫字符串 | 不寫 \0。成功非負,失敗 EOF |
getc/putc | 宏版 fgetc/fputc | 同功能,可能為宏 | 參數求值副作用風險,優先用函數版 |
getchar/putchar | 宏 getc(stdin)/putc(c,stdout) | 標準輸入輸出 | 同上 |
tmpfile | FILE* tmpfile(void) | 創建臨時二進制文件 | 自動刪除於關閉/程序結束。模式 wb+ |
remove | int remove(const char* path) | 刪除文件 | 成功 0。目錄需 rmdir 遞歸 |
rename | int rename(const char* old, const char* new) | 重命名/移動 | 跨文件系統可能失敗 |
// 寫
FILE *fp = fopen("config.ini", "w");
fprintf(fp, "port=%d\n", 8080);
fprintf(fp, "host=%s\n", "127.0.0.1");
fclose(fp);
// 讀
fp = fopen("config.ini", "r");
int port; char host[64];
fscanf(fp, "port=%d\n", &port);
fscanf(fp, "host=%63s\n", host);
fclose(fp);
struct Config { int port; char host[64]; double timeout; };
struct Config cfg = {8080, "127.0.0.1", 30.5};
// 寫
FILE *fp = fopen("config.bin", "wb");
fwrite(&cfg, sizeof(cfg), 1, fp);
fclose(fp);
// 讀
fp = fopen("config.bin", "rb");
struct Config rcfg;
fread(&rcfg, sizeof(rcfg), 1, fp);
fclose(fp);
// 注意:跨平台/編譯器需處理對齊、大端序、類型大小差異
FILE *fp = fopen("data.bin", "rb");
fseek(fp, 0, SEEK_END);
long fsize = ftell(fp);
fseek(fp, 0, SEEK_SET); // 歸零繼續讀
fclose(fp);
char line[1024];
while (fgets(line, sizeof(line), fp)) {
// 處理 line,含換行符
line[strcspn(line, "\n")] = 0; // 去除換行
}
feof 誤用:while(!feof(fp)) fread(...) 會多讀一次。正確:**以讀取函數返回值為循環條件**。fscanf(fp, "%s", buf) 遇空白停止,**無法讀含空格字符串**。改用 fgets 或 %[^\n] 掃描集。fflush 或設置無緩衝 setvbuf(fp, NULL, _IONBF, 0)。fileno(fp) 獲取 fd,但隨後直接 read/write 會破壞 FILE* 內部緩衝狀態。**二選一,不要混用**。C 文件操作核心是 FILE* 流抽象。掌握「模式選擇、格式化 vs 二進制、定位查詢、緩衝控制、錯誤判斷」五大維度,並避開 feof、fscanf 空白截斷、二進制可移植性等經典坑,即可編寫健壯、高效的文件處理代碼。