- gf25030 的博客
scanf
- @ 2026-5-29 20:09:19
C++ scanf 完全指南:格式化输入详解
前言
scanf 是 C/C++ 中最常用的格式化输入函数,与 printf 相对应。它可以从标准输入(键盘)读取数据并按照指定格式进行解析。本文将全面介绍 scanf 的用法、注意事项和常见陷阱。
1. 基本语法
#include <cstdio> // C++ 中使用 cstdio
int scanf(const char* format, ...);
返回值:成功匹配并赋值的参数个数,失败返回 EOF(-1)。
2. 格式说明符
2.1 常用格式符
| 格式符 | 对应类型 | 说明 | 示例 |
|---|---|---|---|
%d |
int* |
十进制整数 | scanf("%d", &num); |
%ld |
long* |
长整型 | scanf("%ld", &num); |
%lld |
long long* |
长长整型 | scanf("%lld", &num); |
%u |
unsigned int* |
无符号整型 | scanf("%u", &num); |
%f |
float* |
单精度浮点数 | scanf("%f", &num); |
%lf |
double* |
双精度浮点数 | scanf("%lf", &num); |
%Lf |
long double* |
长双精度 | scanf("%Lf", &num); |
%c |
char* |
单个字符 | scanf("%c", &ch); |
%s |
字符串(不含空格) | scanf("%s", str); |
|
%p |
void** |
指针地址 | scanf("%p", &ptr); |
%x / %X |
int* |
十六进制整数 | scanf("%x", &num); |
%o |
八进制整数 | scanf("%o", &num); |
|
%[] |
char* |
字符集合 | scanf("%[^\n]", str); |
2.2 修饰符
// 宽度限制:最多读取 n 个字符
char str[100];
scanf("%10s", str); // 最多读取10个字符
// 忽略赋值:读取但不存储
int num;
scanf("%*d %d", &num); // 跳过第一个整数,读取第二个
// h 修饰符:short 类型
short s;
scanf("%hd", &s);
// hh 修饰符:char 类型(作为整数)
char c;
scanf("%hhd", &c);
3. 基础示例
3.1 单个变量输入
#include <cstdio>
int main() {
int age;
double height;
char grade;
printf("请输入年龄、身高、等级:");
scanf("%d %lf %c", &age, &height, &grade);
printf("年龄: %d, 身高: %.2f, 等级: %c\n",
age, height, grade);
return 0;
}
3.2 多个变量输入
#include <cstdio>
int main() {
int a, b, c;
printf("请输入三个整数(用空格或回车分隔):");
scanf("%d %d %d", &a, &b, &c);
printf("总和: %d, 平均值: %.2f\n",
a + b + c, (a + b + c) / 3.0);
return 0;
}
3.3 字符串输入
#include <cstdio>
int main() {
char name[50];
char city[50];
printf("请输入姓名:");
scanf("%s", name); // 注意:不需要 &,name 本身就是地址
printf("请输入城市:");
scanf("%s", city);
printf("你好,%s 来自 %s\n", name, city);
return 0;
}
4. 高级用法
4.1 读取带空格的字符串
#include <cstdio>
int main() {
char sentence[100];
// 方法1:使用 %[^\n] 读取直到换行符
printf("请输入一句话(可包含空格):");
scanf(" %[^\n]", sentence); // 前面的空格跳过残留的换行符
printf("你输入的是:%s\n", sentence);
// 方法2:使用 %[^x] 读取直到遇到特定字符
char data[100];
scanf("%[^,]", data); // 读取直到遇到逗号
return 0;
}
4.2 字符集合匹配
#include <cstdio>
int main() {
char hex[20];
char only_letters[50];
// 只读取十六进制字符
printf("请输入十六进制数:");
scanf("%[0-9a-fA-F]", hex);
printf("读取的十六进制: %s\n", hex);
// 只读取字母
printf("请输入字母(遇到非字母停止):");
scanf("%[A-Za-z]", only_letters);
printf("读取的字母: %s\n", only_letters);
return 0;
}
4.3 抑制赋值和宽度限制
#include <cstdio>
int main() {
int year, month, day;
// 跳过前两个数字,读取第三个
printf("请输入日期(格式:2024-01-15):");
scanf("%*d-%*d-%d", &day);
printf("日: %d\n", day);
// 宽度限制防止溢出
char username[10];
printf("请输入用户名(最多9个字符):");
scanf("%9s", username);
printf("用户名: %s\n", username);
return 0;
}
4.4 混合格式输入
#include <cstdio>
int main() {
int id;
char name[30];
float score;
// 按特定格式读取
printf("请输入(格式:ID,姓名,成绩):");
scanf("%d,%[^,],%f", &id, name, &score);
printf("ID: %d, 姓名: %s, 成绩: %.1f\n",
id, name, score);
return 0;
}
5. 返回值检查
5.1 错误处理
#include <cstdio>
int main() {
int num;
int result = scanf("%d", &num);
if (result == 1) {
printf("成功读取整数: %d\n", num);
} else if (result == 0) {
printf("输入不匹配,未能读取整数\n");
} else if (result == EOF) {
printf("输入结束或发生错误\n");
}
return 0;
}
5.2 循环读取
#include <cstdio>
int main() {
int num;
int sum = 0;
int count = 0;
printf("请输入多个整数(输入非数字结束):\n");
while (scanf("%d", &num) == 1) {
sum += num;
count++;
}
if (count > 0) {
printf("共输入 %d 个数,总和: %d,平均值: %.2f\n",
count, sum, (double)sum / count);
}
return 0;
}
6. 常见陷阱与解决方案
6.1 缓冲区残留问题
#include <cstdio>
int main() {
int num;
char ch;
// ❌ 问题代码
printf("输入一个整数:");
scanf("%d", &num);
printf("输入一个字符:");
scanf("%c", &ch); // 会读取残留的换行符!
// ✅ 解决方案
printf("输入一个整数:");
scanf("%d", &num);
getchar(); // 消耗掉换行符
printf("输入一个字符:");
scanf("%c", &ch);
// 或者使用空格跳过空白字符
printf("输入一个整数:");
scanf("%d", &num);
printf("输入一个字符:");
scanf(" %c", &ch); // 空格会跳过所有空白字符
return 0;
}
6.2 字符串缓冲区溢出
#include <cstdio>
int main() {
char buffer[10];
// ❌ 危险:可能溢出
// scanf("%s", buffer);
// ✅ 安全:限制宽度
scanf("%9s", buffer); // 最多读取9个字符,留一个给 '\0'
return 0;
}
6.3 浮点数格式混淆
#include <cstdio>
int main() {
float f;
double d;
// ❌ 错误:%f 对应 float,%lf 对应 double
// scanf("%f", &d); // 错误!
// scanf("%lf", &f); // 错误!
// ✅ 正确
scanf("%f", &f); // float 使用 %f
scanf("%lf", &d); // double 使用 %lf
return 0;
}
6.4 混合 cin 和 scanf
#include <iostream>
#include <cstdio>
int main() {
int num;
// ⚠️ 混用可能导致问题
std::cin >> num;
scanf("%d", &num); // 可能跳过或读取错误
// ✅ 解决方案:统一使用一种方式
// 或使用 ios::sync_with_stdio(false) 同步
return 0;
}
7. 实用示例
7.1 读取未知数量的整数
#include <cstdio>
int main() {
int arr[100];
int n = 0;
printf("请输入一组整数(Ctrl+D/Ctrl+Z 结束):\n");
while (scanf("%d", &arr[n]) == 1 && n < 100) {
n++;
}
printf("共读取 %d 个数:", n);
for (int i = 0; i < n; i++) {
printf("%d ", arr[i]);
}
printf("\n");
return 0;
}
7.2 解析时间格式
#include <cstdio>
int main() {
int h, m, s;
printf("请输入时间(格式:HH:MM:SS):");
if (scanf("%d:%d:%d", &h, &m, &s) == 3) {
printf("时: %d, 分: %d, 秒: %d\n", h, m, s);
printf("总秒数: %d\n", h * 3600 + m * 60 + s);
} else {
printf("格式错误!\n");
}
return 0;
}
7.3 简单计算器
#include <cstdio>
int main() {
double a, b;
char op;
printf("请输入表达式(如:3.5 + 2.1):");
if (scanf("%lf %c %lf", &a, &op, &b) == 3) {
switch (op) {
case '+': printf("%.2f\n", a + b); break;
case '-': printf("%.2f\n", a - b); break;
case '*': printf("%.2f\n", a * b); break;
case '/':
if (b != 0) printf("%.2f\n", a / b);
else printf("除数不能为零!\n");
break;
default: printf("不支持的运算符!\n");
}
} else {
printf("输入格式错误!\n");
}
return 0;
}
8. 快速参考卡片
// 基本类型
scanf("%d", &i); // int
scanf("%ld", &l); // long
scanf("%lld", &ll); // long long
scanf("%u", &u); // unsigned int
scanf("%f", &f); // float
scanf("%lf", &d); // double
scanf("%c", &c); // char
scanf("%s", s); // 字符串(无空格)
scanf("%p", &p); // 指针
// 修饰符
scanf("%10s", s); // 宽度限制
scanf("%*d", &i); // 忽略输入
scanf("%hd", &s); // short
scanf("%hhd", &c); // char(整数)
scanf("%[^\n]", s); // 读取整行
scanf("%[0-9]", s); // 只读数字
// 返回值检查
int result = scanf(...);
if (result == EOF) { /* 错误或文件结束 */ }
if (result == 0) { /* 匹配失败 */ }
if (result == n) { /* 成功读取 n 个值 */ }
9. 最佳实践总结
- 总是检查返回值:确认成功读取了期望数量的输入
- 限制字符串宽度:防止缓冲区溢出
- 注意缓冲区残留:使用
getchar()或" %c"处理换行符 - 统一输入方式:避免混用
cin和scanf - 使用正确的格式符:
float用%f,double用%lf - 初始化变量:防止读取失败时使用未初始化的值
结语
scanf 是强大而灵活的输入函数,但也需要谨慎使用。掌握格式说明符、理解缓冲区行为、做好错误处理,就能安全高效地使用它。记住本文的最佳实践,避免常见陷阱!
参考资料