C语言学习笔记20260713-格式化输入输出家族汇总
核心概念:这组函数本质相同,区别仅在于数据源(Source)和目的地(Destination)不同。
头文件:<stdio.h>
核心对比总览
| 标准 I/O | scanf | 键盘 (stdin) | 内存变量 | 用户交互输入 |
| printf | 内存变量 | 屏幕 (stdout) | 打印结果、日志 | |
| 文件 I/O | fscanf | 文件指针 (FILE*) | 内存变量 | 读取配置文件、解析文本数据 |
| fprintf | 内存变量 | 文件指针 (FILE*) | 写入日志文件、保存数据 | |
| 字符串 I/O | sscanf | 字符串 (char*) | 内存变量 | 解析字符串中的特定数据(如提取 IP) |
| sprintf | 内存变量 | 字符串 (char*) | 拼接字符串、数字转字符串 |
1. 标准输入输出:人机交互
这是最基础的函数,默认绑定到控制台(黑窗口)。
- scanf:等待用户在键盘敲击并回车,将数据读入变量。
- printf:将变量的内容显示在屏幕上。
代码示例
#include <stdio.h>
int main() {
int age;
// printf: 提示用户输入
printf("请输入你的年龄: ");
// scanf: 从键盘读取整数存入 age
// 注意:这里必须加 & 取地址符
if (scanf("%d", &age) == 1) {
// printf: 格式化输出结果
printf("你今年 %d 岁了。\\n", age);
} else {
printf("输入无效!\\n");
}
return 0;
}
2. 文件流操作:持久化存储
这两个函数比标准 I/O 多了一个参数 FILE *stream,用于指定操作哪个文件。
- fscanf:从文件中按格式读取数据。
- fprintf:按格式将数据写入文件。
代码示例
#include <stdio.h>
int main() {
FILE *fp = fopen("data.txt", "w+");
if (!fp) return 1;
char name[50] = "Alice";
int score = 98;
// fprintf: 将数据格式化写入文件
// 类似于 printf,但第一个参数是文件指针
fprintf(fp, "Name: %s, Score: %d\\n", name, score);
// 重置文件指针到开头以便读取
rewind(fp);
char read_name[50];
int read_score;
// fscanf: 从文件读取格式化数据
// 类似于 scanf,但第一个参数是文件指针
if (fscanf(fp, "Name: %s, Score: %d", read_name, &read_score) == 2) {
printf("读取成功 -> 姓名: %s, 分数: %d\\n", read_name, read_score);
}
fclose(fp);
return 0;
}
3. 字符串流操作:内存中的转换
这两个函数不依赖外部设备或文件,直接在内存中的字符数组(字符串)进行操作。
- sscanf (String Scan):从一个已存在的字符串中提取数据。
- sprintf (String Print):将各种类型的数据拼接到一个字符串中。
场景 A:使用 sscanf 解析复杂字符串
假设你有一个字符串 "2023-10-05",想把它拆分成年、月、日。
#include <stdio.h>
int main() {
const char *date_str = "2023-10-05";
int year, month, day;
// sscanf: 从 date_str 中按 "-" 分割提取整数
// 返回值是成功匹配并赋值的变量个数
int count = sscanf(date_str, "%d-%d-%d", &year, &month, &day);
if (count == 3) {
printf("解析成功: %d 年 %d 月 %d 日\\n", year, month, day);
}
return 0;
}
场景 B:使用 sprintf 拼接/转换字符串
假设你想把多个变量拼成一句话,或者把数字变成字符串。
#include <stdio.h>
int main() {
char buffer[100]; // 目标缓冲区,必须足够大
int id = 1001;
double price = 99.5;
// sprintf: 将数据格式化写入 buffer 字符串
// 注意:如果数据太长超过 buffer 大小,会发生溢出(危险!)
sprintf(buffer, "ID: %d, Price: %.2f USD", id, price);
printf("生成的字符串: %s\\n", buffer);
// 应用:数字转字符串
char num_str[20];
sprintf(num_str, "%d", 12345);
printf("数字转字符串: %s\\n", num_str);
return 0;
}
⚠️ 安全提示与最佳实践
sprintf 的隐患:
- sprintf 不检查目标缓冲区的大小,容易导致缓冲区溢出。
- 推荐替代方案:使用 snprintf(buffer, size, …),它可以限制写入的最大长度,更加安全。
// 安全写法:限制最多写入 sizeof(buffer)-1 个字符
snprintf(buffer, sizeof(buffer), "ID: %d", id);
scanf 的隐患:
- 读取字符串时如果不限制长度(如 %s),用户输入过长也会导致溢出。
- 推荐写法:scanf("%19s", str)(假设 str 大小为 20)。
返回值检查:
- 所有的 scanf 系列函数都会返回成功匹配并赋值的项数。务必检查返回值以确保数据解析正确。
总结
C 语言的格式化输入输出家族提供了灵活的数据处理能力:
- 标准 I/O 用于人机交互
- 文件 I/O 用于数据持久化
- 字符串 I/O 用于内存中的数据转换和解析
掌握这些函数的区别和使用场景,能够让你在处理各种数据输入输出任务时更加得心应手。记住安全第一,始终检查返回值并使用安全的替代函数(如 snprintf)。




