| 類別 | 函數 | 用途 |
| 字符 I/O | fgetc/fputc | 單字符讀寫 |
fgets/fputs | 行/字符串讀寫 |
getc/putc | 宏版本,可能更快 |
| 格式化 I/O | fprintf/fscanf | 類 printf/scanf,針對文件流 |
fscanf 返回值 | 成功匹配/賦值的項目數,EOF 表示失敗/結束 |
fprintf 返回值 | 寫入字符數 |
| 二進制塊 I/O | fread/fwrite | size * count 字節,返回完成項目數 |
| 結構體直接讀寫 | fwrite(&struct, sizeof(struct), 1, fp) |
| 注意對齊/填充 | 跨平台需打包 (#pragma pack) 或序列化 |
| 隨機訪問 | fseek(fp, offset, origin) | origin: SEEK_SET/CUR/END |
ftell(fp) | 返回當前位置 (long) |
rewind(fp) | 等價 fseek(fp, 0, SEEK_SET) + 清除錯誤標誌 |
fgetpos/fsetpos | 大文件支持 (fpos_t) |
| 狀態檢查 | feof/ferror/clearerr | 檢查結束/錯誤/清除標誌 |
| 關閉 | fclose(fp) | 刷新緩衝區、釋放資源,返回 0 成功 |
四、完整示例程序 (cFile.c)
#include "cFile.h"
#include "cHelper.h" // 假設提供 str_concat2 等輔助
void file_demo1_write_text(const char *root) {
char *path = str_concat2(root, "\\c_file_demo1.txt");
FILE *fp = fopen(path, "wt+"); // 文本讀寫,創建/截斷
if (!fp) { /* errno 處理 */ free(path); return; }
fputc('w', fp);
fputs("ebabcd\nwanglei", fp);
fclose(fp);
free(path);
}
void file_demo2_read_text(const char *root) {
char *path = str_concat2(root, "\\c_file_demo1.txt");
FILE *fp = fopen(path, "rt");
if (!fp) { free(path); return; }
char ch;
while ((ch = fgetc(fp)) != EOF) { /* 處理字符 */ }
rewind(fp);
char line[32];
while (fgets(line, sizeof(line), fp)) { /* 處理行 */ }
fclose(fp);
free(path);
}
void file_demo3_formatted(const char *root) {
char *path = str_concat2(root, "\\c_file_demo2.txt");
FILE *fp = fopen(path, "wt+");
if (!fp) { free(path); return; }
fprintf(fp, "%s %d %d\n", "webabcd", 100, 20); // 寫入 15 字節
rewind(fp);
char name[32]; int num, age;
fscanf(fp, "%s %d %d\n", name, &num, &age); // 讀回 3 項
fclose(fp);
free(path);
}
void file_demo4_binary(const char *root) {
struct Employee { int num; char name[32]; };
struct Employee w[2] = {{100, "wanglei"}, {200, "webabcd"}}, r[2];
char *path = str_concat2(root, "\\c_file_demo3.b");
FILE *fp = fopen(path, "wb+");
if (!fp) { free(path); return; }
fwrite(w, sizeof(struct Employee), 2, fp); // 寫入 2 個結構體
rewind(fp);
fread(r, sizeof(struct Employee), 2, fp); // 讀回
fclose(fp);
free(path);
}
void file_demo5_random_access(const char *root) {
struct Employee { int num; char name[32]; };
struct Employee w[2] = {{100, "wanglei"}, {200, "webabcd"}}, r[1];
char *path = str_concat2(root, "\\c_file_demo4.b");
FILE *fp = fopen(path, "wb+");
if (!fp) { free(path); return; }
fwrite(w, sizeof(struct Employee), 2, fp);
fseek(fp, sizeof(struct Employee), SEEK_SET); // 跳過第1個,定位第2個
fread(r, sizeof(struct Employee), 1, fp); // 只讀第2個
// r[0] = {200, "webabcd"}
fclose(fp);
free(path);
}
char *demo_cFile(char *rootPath) {
file_demo1_write_text(rootPath);
file_demo2_read_text(rootPath);
file_demo3_formatted(rootPath);
file_demo4_binary(rootPath);
file_demo5_random_access(rootPath);
return str_concat2("演示文件保存路徑:", rootPath);
}
五、錯誤處理鐵律
- 每個 fopen 必須檢查 NULL,失敗時讀取
errno (包含 errno.h)。
- 讀寫後檢查
ferror(fp),非 0 表示硬件/介質錯誤。
- 調用
clearerr(fp) 清除標誌後方可繼續操作。
- 嚴格配對
fclose,即使出錯也要關閉 (或用 goto cleanup 模式)。
- 二進制模式用
rb/wb,避免 Windows 換行符轉換損壞數據。
六、Windows 特有注意事項
- 路徑分隔符:
\\ 或 / (MSVC 支持正斜槓)。
- 大文件 (>2GB):使用
_fseeki64/_ftelli64 或 fgetpos/fsetpos。
- Unicode 路徑:
_wfopen(L"path", L"mode") 配合寬字符。
- 文件共享模式:
_fsopen 可指定 _SH_DENYRW 等共享屬性。
掌握文本/二進制模式差異、格式化與塊 I/O 選擇、隨機訪問定位、完整錯誤處理鏈路,是 Windows C 開發者處理持久化數據的基本功。示例代碼涵蓋所有核心場景,可直接作為項目文件模塊基礎。
- 上一篇文章: Qtun 專案實戰:嵌入 Lua 腳本引擎實現靈活配置與插件擴展
- 下一篇文章: 深入掌握 C 語言可變參數:stdarg.h 完整指南
Copyright ©
程式碼之家 All Rights Reserved