scanf、sscanf 和 fscanf 是 C 语言中用于格式化输入的函数,它们虽然数据来源不同,但有许多相同点。以下是它们的共同特性:
1. 相同的函数原型结构
三者的函数原型非常相似,核心区别仅在于数据来源:
int scanf(const char *format, ...); // 从标准输入(stdin)读取
int fscanf(FILE *stream, const char *format, ...); // 从文件流读取
int sscanf(const char *str, const char *format, ...); // 从字符串读取
format:格式化字符串,指定如何解析输入(如%d、%f、%s等)。...:可变参数,用于存储解析后的数据(必须传递变量的地址)。
2. 相同的格式化字符串规则
它们使用完全相同的格式说明符,例如:
|
格式说明符 |
作用 |
|
|
读取 |
|
|
读取 |
|
|
读取 |
|
|
读取单个字符 |
|
|
读取字符串(遇空格/换行停止) |
|
|
读取一行(直到换行符) |
|
|
匹配 |
int num;
float f;
char str[100];
// scanf
scanf("%d %f %s", &num, &f, str); // 从键盘读取
// fscanf
FILE *file = fopen("data.txt", "r");
fscanf(file, "%d %f %s", &num, &f, str); // 从文件读取
// sscanf
char input[] = "42 3.14 hello";
sscanf(input, "%d %f %s", &num, &f, str); // 从字符串解析
3. 相同的返回值
成功时:返回成功匹配并赋值的输入项的数量。
int a, b;
int count = scanf("%d %d", &a, &b); // 输入 "10 20" → 返回 2
失败时:
- 如果输入不匹配(如输入
abc但期望%d),返回0。 - 如果到达文件末尾(
fscanf)或字符串结束(sscanf),返回EOF(通常是-1)。
4. 相同的输入解析行为
空白符处理:
三者都会跳过前导空白字符(空格、制表符、换行符),直到遇到有效数据。
// 输入 " 42" 会被正确解析为 42
scanf("%d", &num);
匹配规则:
- 遇到不符合格式的数据时立即停止(如
%d遇到字母)。 - 未匹配的数据会留在输入源(键盘缓冲区、文件或字符串)中。
5. 相同的安全性问题
缓冲区溢出风险:
三者均不检查目标变量的大小,尤其是 %s 和 %[^\n]:
char str[5];
scanf("%s", str); // 输入 "abcdef" → 溢出!
解决方法:
- 使用宽度限制(如
%4s最多读取 4 个字符)。 - 更安全的替代方案:
fgets+sscanf。
换行符残留问题(scanf 特有):
scanf("%d", &num); // 输入 "42\n",换行符留在缓冲区
getchar(); // 清除换行符
6. 相同的底层实现
- 三者均基于 C 标准库的格式化输入解析引擎,只是数据来源不同:
-
scanf→stdin(键盘)fscanf→FILE*(文件)sscanf→const char*(字符串)
总结:三者的相同点
|
特性 |
|
|
|
|
函数原型结构 |
✔️ |
✔️ |
✔️ |
|
格式化字符串规则 |
✔️ |
✔️ |
✔️ |
|
返回值逻辑 |
✔️ |
✔️ |
✔️ |
|
输入解析行为 |
✔️ |
✔️ |
✔️ |
|
缓冲区溢出风险 |
✔️ |
✔️ |
✔️ |
|
底层实现机制 |
✔️ |
✔️ |
✔️ |
何时选择哪个函数?
scanf:仅用于简单的交互式输入(不推荐生产代码)。fscanf:从文件读取结构化数据(如配置文件、日志)。sscanf:解析字符串(如fgets读取的行或网络数据)。
最佳实践:
- 优先使用
fgets+sscanf组合,避免scanf的安全问题。 - 文件操作时用
fscanf,但需检查返回值。 - 复杂字符串解析用
sscanf,可结合%[^,]等高级格式。

被折叠的 条评论
为什么被折叠?



