C 语言格式化读写
更新时间:2026-08-26。本文是
languages/c/主题入门层第 63 篇。程序间交换数据,最朴素的格式就是文本:"1001 alice 95.5"。fprintf按格式写出、fscanf按格式读回——一写一读构成完整的"文本序列化"。但要小心:格式化解析文本,格式串一错,数据全歪。
本文要回答的问题
- 怎么把结构体数据格式化写入文件?
- 怎么从文件按格式读回来?
fscanf解析有什么局限?
一、fprintf:把数据写成文本
c
typedef struct { int id; char name[32]; double score; } Student;
Student s = {1001, "alice", 95.5};
FILE *fp = fopen("students.txt", "w");
fprintf(fp, "%d %s %.2f\n", s.id, s.name, s.score);
fclose(fp);文件里就是一行文本:
1001 alice 95.50写出的格式自己定义,但必须"自洽"——写的时候用什么格式,读的时候就要用什么格式解析。
二、fscanf:按格式读回
c
FILE *fp = fopen("students.txt", "r");
Student s;
if (fscanf(fp, "%d %s %lf", &s.id, s.name, &s.score) == 3) {
printf("%d %s %.2f\n", s.id, s.name, s.score);
}
fclose(fp);与 printf 对称,三个要点:
- 写
%d读%d,写%.2f读%lf(double 要l前缀); - 要传地址:
&s.id、s.name(数组名本身是地址); - 返回值 = 成功匹配的项数,用它判断是否读完/读对。
三、解析返回值:读对没读对
fscanf 返回成功转换的参数个数,这是判断解析是否成功的唯一可靠依据:
c
while (fscanf(fp, "%d %s %lf", &id, name, &score) == 3) {
/* 每一行解析成功 */
}- 文件读完了返回
EOF; - 格式不匹配(比如该数字却遇到字母)返回实际匹配数(< 3)——此时该行没读完,下次循环会从错误位置继续,很容易死循环,要小心。
四、sscanf / sprintf:解析字符串,不碰文件
不经过文件,直接在内存里格式化:
c
char line[] = "1001 alice 95.5";
Student s;
sscanf(line, "%d %s %lf", &s.id, s.name, &s.score); // 从字符串读
char out[128];
sprintf(out, "%d %s %.2f\n", s.id, s.name, s.score); // 写入字符串snprintf(带长度限制的版本)更安全,防止目标缓冲区不够大——写 sprintf 前先想一下有没有可能溢出。
五、fscanf 的局限:它怕"变"与"错"
| 局限 | 说明 |
|---|---|
| 空白分隔固定 | 名字里有空格("Mary Jane")就解析错 |
| 严格按格式 | 行尾多了字段、少了个字段都错位 |
| 错误恢复差 | 一行解析失败,位置前进不确定 |
| 跨行混乱 | 把换行当空白,可能"串行" |
对策:
- 结构规整(无空格、固定字段)→ fscanf 足够;
- 字段带空格/变长 → 用
fgets读整行 + 手工拆分(strtok)或正则; - 复杂格式 → 别用 C 手搓,考虑 JSON/CSV 库。
六、常见坑对照
| 坑 | 现象 | 对策 |
|---|---|---|
| 忘记 & | 段错误(写了指针值) | 变量传 & |
| %f 读 double | 数据错乱 | double 用 %lf |
| 格式串与数据不对称 | 解析错位 | 写读格式成对设计 |
| 不检查返回值 | 解析到垃圾 | 检查 == 期望项数 |
| 用 sprintf | 缓冲区溢出 | 用 snprintf |
七、与本站主线衔接
- 读行与手工解析,见字符与行读取;
- 完整的学生管理系统(fscanf 读档),见学生成绩管理系统;
- 二进制序列化的对比,见二进制读写 fread/fwrite。
一句话总结
fprintf 按格式写文本、fscanf 按格式读回(要传 &、double 用 %lf、看返回值判断解析成败),sscanf/snprintf 是内存版——格式串成对设计、解析必查返回值;结构规整的文本用它,字段复杂的就别硬撑了。