8
文件 IO:让数据活过程序这次运行
File I/O with FILE*
程序一关,变量全没。想把成绩、配置存下来下次还能读,就得写文件。C 的文件操作核心是一个 FILE* 指针——"先打开,读/写,最后关上",三步曲。
打开与关闭:fopen / fclose
| 模式 | 含义 |
|---|---|
"r" | 只读,文件必须已存在。 |
"w" | 只写,文件不存在就新建,存在就清空(小心!)。 |
"a" | 追加,在文件末尾接着写,不清空旧内容。 |
"r+" | 读写,文件须存在,不清空。 |
加 b(如 "rb") | 二进制模式,处理图片/视频等非文本文件时用。 |
写文件再读回来(文本模式)
#include <stdio.h>
int main() {
// 1. 打开(写模式)
FILE *fp = fopen("score.txt", "w");
if (fp == NULL) { // 打不开(路径错/没权限)要判空
perror("打开失败");
return 1;
}
// 2. 写入:fprintf 和 printf 几乎一样,只是多了 fp
fprintf(fp, "小明 %d %.1f\n", 18, 92.5);
fclose(fp); // 3. 用完必须关!
// 重新以读模式打开
fp = fopen("score.txt", "r");
char name[20]; int age; double score;
fscanf(fp, "%s %d %lf", name, &age, &score); // 注意 age、score 要传地址
printf("读回:%s 年龄%d 成绩%.1f\n", name, age, score);
fclose(fp);
return 0;
}
运行结果
读回:小明 年龄18 成绩92.5
其余常用文件函数速查
| 函数 | 用途 |
|---|---|
fgetc / fputc | 读写一个字符。 |
fgets / fputs | 读写一行字符串。读文本行用它,别用 gets。 |
fread / fwrite | 二进制读写整块数据(数组、结构体),最快。 |
fseek / ftell / rewind | 移动文件指针位置、查当前位置、回到开头。 |
stdin / stdout / stderr | 标准输入、标准输出、标准错误,三个默认打开的流。 |
错误处理:errno / perror / strerror 三件套
C 库函数出错时不抛异常,而是返回一个错误值(通常 -1 或 NULL),并把错误原因编号写进全局变量 errno。你要做的是:先看返回值,再读 errno 知道为啥错。
fopen 失败时的标准错误处理三件套
#include <errno.h>
#include <string.h>
#include <stdio.h>
FILE *fp = fopen("not_exist.txt", "r");
if (fp == NULL) {
// ① perror:直接把 errno 对应的人话打出来
perror("打开文件失败"); // 打印:打开文件失败: No such file or directory
// ② errno 是个整数错误码
printf("错误码 = %d\n", errno); // 2(ENOENT)
// ③ strerror(errno):拿到错误描述字符串,自己格式化用
printf("原因:%s\n", strerror(errno));
return 1;
}
| 工具 | 干什么 |
|---|---|
errno | 全局 int,出错时被库函数赋值。注意:成功调用不会清 0,所以要先看返回值错了再读 errno。 |
perror("前缀") | 一句话打印:前缀 + 冒号 + errno 对应描述。最省事。 |
strerror(errno) | 返回错误描述字符串,想自己拼日志就用它。 |
feof(fp) / ferror(fp) | feof 判断是否读到了文件末尾;ferror 判断流是否出错。读文件循环别只靠 feof,要用 fread 的返回值判断是否读到数据。 |
防坑:① errno 是个"垃圾桶"——上一次的错误可能还留着,必须在函数返回错误后立刻读,中间别插别的库调用;② 读文件的正确姿势是 while ((n = fread(...)) > 0) {...},而不是 while(!feof(fp))(feof 要等读失败后才置位,会多读一次空)。