7、格式化读写函数:fscanf与fprintf函数详解
好,咱们今天聊聊格式化读写。说白了,就是带格式的输入输出。
你平时用 printf 和 scanf 跟控制台打交道,对吧?那如果我想把同样的格式化数据写到文件里,或者从文件里读出来呢?这时候就该 fprintf 和 fscanf 登场了。
7.1 函数原型与参数
先看原型,其实特别简单:
#include <stdio.h>
int fprintf(FILE *stream, const char *format, ...);
int fscanf(FILE *stream, const char *format, ...);
跟 printf/scanf 比,就多了一个参数——文件指针 FILE *stream。其他一模一样。
我个人习惯把这两个函数叫做「带文件版的格式化IO」。你想想看,printf 其实等价于 fprintf(stdout, ...),scanf 等价于 fscanf(stdin, ...)。嗯,底层就是这么干的。
| 函数 | 输出目标 | 输入来源 |
|---|---|---|
| printf / scanf | 标准输出(屏幕) | 标准输入(键盘) |
| fprintf / fscanf | 任意文件流 | 任意文件流 |
7.2 格式化写入示例
我直接上代码。假设我们要记录一批传感器的数据:
#include <stdio.h>
typedef struct {
int id;
float temperature;
float humidity;
} SensorData;
int main() {
FILE *fp = fopen("sensor_log.txt", "w");
if (fp == NULL) {
perror("文件打开失败");
return 1;
}
SensorData data[] = {
{1, 25.6, 60.2},
{2, 26.1, 58.7},
{3, 24.8, 62.3}
};
for (int i = 0; i < 3; i++) {
fprintf(fp, "%d %.1f %.1f\n",
data[i].id,
data[i].temperature,
data[i].humidity);
}
fclose(fp);
printf("数据写入完成\n");
return 0;
}
运行后,sensor_log.txt 里的内容是这样的:
1 25.6 60.2
2 26.1 58.7
3 24.8 62.3
格式清晰,每行一条记录。我在项目中经常用这种方式做数据导出,方便后续用 Excel 或 Python 分析。
7.3 格式化读取示例
读回来也很直接:
#include <stdio.h>
int main() {
FILE *fp = fopen("sensor_log.txt", "r");
if (fp == NULL) {
perror("文件打开失败");
return 1;
}
int id;
float temp, hum;
int count = 0;
while (fscanf(fp, "%d %f %f", &id, &temp, &hum) == 3) {
printf("记录%d: ID=%d, 温度=%.1f, 湿度=%.1f\n",
++count, id, temp, hum);
}
fclose(fp);
return 0;
}
关键点:fscanf 返回成功匹配并赋值的参数个数。如果返回 3,说明三个字段都读对了。如果返回 EOF 或小于 3,说明文件结束或格式不匹配。
7.4 与 scanf/printf 的对比
我整理了一张对比表,你一看就明白:
| 对比维度 | printf / scanf | fprintf / fscanf |
|---|---|---|
| 操作对象 | 标准 I/O(stdin/stdout) | 任意文件流 |
| 灵活性 | 固定,不能重定向 | 可操作任意打开的文件 |
| 错误处理 | 通常忽略返回值 | 必须检查返回值 |
| 使用场景 | 控制台交互、调试输出 | 文件读写、日志记录、数据持久化 |
| 缓冲区 | 行缓冲(stdout)或无缓冲(stderr) | 全缓冲(除非设置) |
这里有个坑,我曾经踩过:fscanf 遇到空格、换行符会自动跳过,但如果你格式字符串里写错了匹配符,比如用 %d 去读一个字母,那就会匹配失败,文件指针卡在那里不动了。
避坑指南:我曾经在解析一个配置文件时,文件里混入了中文字符,fscanf 直接罢工。后来我加了个检查:每次读取后判断返回值,如果小于预期,就用 fgetc 跳过当前字符,再重试。这招叫「容错读取」,嵌入式环境里特别实用。
7.5 格式化读写的核心逻辑
下面这张图,是我自己总结的格式化读写流程,你看一眼就能记住:
7.6 实际项目中的经验
我做了这么多年嵌入式,格式化读写用得最多的场景有两个:
- 配置文件解析:比如 WiFi 模块的 SSID 和密码,用
fscanf按格式读,简单可靠。 - 数据日志导出:传感器采集的数据,用
fprintf写成 CSV 格式,上位机直接读。
小技巧:如果你要写二进制数据,别用 fprintf,用 fwrite。格式化函数是给人看的,不是给机器看的。二进制读写效率高得多,但可读性差。看场景选工具。
嗯,关于格式化读写,核心就这些。记住一句话:fprintf 和 fscanf 就是 printf 和 scanf 的文件版,多了一个文件指针参数,多了一份对返回值的敬畏心。
公众号:蓝海资料掘金营,微信deep3321