c 编程/stdio.h/scanf
此页面可能需要 验证事实 并引用可靠的出版物。 您可以通过添加对可靠出版物的引用或 更正被引用为事实的陈述 来提供帮助。 |
scanf 是一个 函数,它从给定的字符串流源中读取指定格式的数据,起源于 C 编程语言,并且存在于许多其他编程语言中。
scanf
函数原型是
int scanf(const char *format, ...);
该函数返回成功匹配的项目总数,这可能少于请求的项目数。如果输入流耗尽或在任何项目匹配之前从该流中读取失败,则返回 EOF。
就可追溯性而言,“scanf” 代表“扫描格式”,因为它扫描输入以查找有效令牌并根据指定的格式解析它们。
scanf
函数存在于 C 中,它从 标准输入(通常是 命令行界面 或类似的 文本用户界面)读取数字和其他 数据类型 的输入。
以下展示了 C 中的代码,该代码从标准输入中读取可变数量的无格式十进制 整数,并在单独的行上打印出每个整数
#include <stdio.h>
int
main(void)
{
int n;
while (scanf("%d", & n))
printf("%d\n", n);
return 0;
}
在由上面的程序处理后,一组杂乱的整数,例如
456 123 789 456 12 456 1 2378
将整齐地显示为
456 123 789 456 12 456 1 2378
打印一个单词
#include <stdio.h>
int
main(void)
{
char word[20];
if(scanf("%19s", word) == 1)
puts(word);
return 0;
}
无论程序员希望程序读取什么数据类型,参数(如上面的 &n
)都必须是 指针,指向内存。否则,该函数将无法正常执行,因为它将尝试覆盖错误的内存区域,而不是指向您试图获取输入的变量的内存位置。
由于 scanf
被指定为仅从标准输入读取,因此许多具有 接口 的编程语言,例如 PHP,有派生函数,如 sscanf
和 fscanf
,但没有 scanf
本身。
根据输入的实际来源,程序员可以使用 scanf
的不同派生函数。两个常见的例子是 sscanf
和 fscanf
。
fscanf 派生函数从指定的 文件流 读取输入。原型如下
int fscanf (FILE *file, const char *format, ...);
(PHP)
mixed fscanf (resource file, const string format [, mixed args...])
fscanf
派生函数的工作方式与原始的 scanf
函数类似 - 一旦读取了输入的一部分,就不会再次读取,直到该文件被关闭并重新打开。
sscanf 派生函数从作为第一个参数传递的字符字符串中读取输入。与 fscanf 的一个重要区别是,每次调用该函数时,都会从字符串的开头读取。没有在成功读取操作后递增的“指针”。原型如下
int sscanf (const char *str, const char *format, ...);
(PHP)
mixed sscanf (const string str, const string format [, mixed args...])
int vscanf (const char *format, va_list args);
int vsscanf (const char *source, const char *format, va_list args);
int vfscanf (FILE *file, const char *format, va_list args);
这些函数与没有 v
前缀的相同函数类似,除了它们从 va_list
获取参数。(参见 stdarg.h。)这些变体可用于可变参数函数。
scanf
中的格式 占位符 与其逆函数 printf
中的格式占位符大致相同。
格式字符串中很少有常量(即不是格式 占位符 的字符),主要是因为程序通常不是为了读取已知数据而设计的。例外是 空白 字符,它会丢弃输入中的所有空白字符。
以下是最常用的占位符
%d
: 将整数扫描为带符号的 十进制 数。%i
: 将整数扫描为带符号的数字。类似于%d
,但在以0x
开头时将其解释为 十六进制,以0
开头时解释为 八进制。例如,字符串031
使用%d
将被读取为 31,使用%i
将被读取为 25。%hi
中的标志h
表示转换为short
,hh
表示转换为char
。%u
: 扫描十进制unsigned int
(请注意,在 C99 标准中,输入值的减号是可选的,因此如果读取负数,则不会出现错误,结果将是 二进制补码。参见strtoul()
.Template:Failed verification) 相应地,%hu
扫描unsigned short
,%hhu
扫描unsigned char
。%f
: 将浮点数扫描为普通 (定点) 表示法。%g
,%G
: 将浮点数扫描为普通或指数表示法。%g
使用小写字母,%G
使用大写字母。%x
,%X
: 将整数扫描为无符号的 十六进制 数。%o
: 将整数扫描为 八进制 数。%s
: 扫描一个字符字符串。扫描在遇到空白字符时结束。一个空字符存储在字符串的末尾,这意味着提供的缓冲区必须比指定的输入长度至少长一个字符。%c
: 扫描一个字符(char)。不添加任何空字符。(space)
: 空格扫描空白字符。%lf
: 扫描为双精度浮点数。%Lf
: 扫描为长双精度浮点数。
以上可以在数字修饰符和表示“long”的l
、L
修饰符之间进行组合使用。在百分号和字母之间,还可以有数值(在任何“long”修饰符之前),指定要扫描的字符数。百分号后面的可选星号 (*
) 表示通过此格式说明符读取的数据不存储在变量中。格式字符串后面的参数不应包含此丢弃的变量。
ff
修饰符在printf中不存在,导致输入和输出模式之间存在差异。ll
和hh
修饰符在C90标准中不存在,但在C99标准中存在。[1]
格式字符串的示例为
"%7d%s %c%lf"
上面的格式字符串将前七个字符扫描为十进制整数,然后读取剩余的字符作为字符串,直到遇到空格、换行符或制表符,然后扫描后面的第一个非空白字符,最后扫描双精度浮点数。
错误处理
[edit | edit source]scanf
通常用于程序无法保证输入是否为预期格式的情况。因此,健壮的程序必须检查scanf
调用是否成功,并采取适当的措施。如果输入格式不正确,错误数据将仍然保留在输入流中,必须先读取并丢弃,才能读取新的输入。另一种避免这种情况的输入读取方法是使用fgets
,然后检查读取的字符串。最后一步可以用sscanf
完成,例如。
安全
[edit | edit source]与printf
类似,scanf
容易受到格式字符串攻击。应格外小心,确保格式字符串包含字符串和数组大小的限制。在大多数情况下,用户输入的字符串大小是任意的;它在执行scanf
函数之前无法确定。这意味着不带长度说明符的%s
占位符本质上是不安全的,可以被利用来进行缓冲区溢出。另一个潜在问题是允许动态格式字符串,例如存储在配置文件或其他用户控制文件中的格式字符串。在这种情况下,除非事先检查格式字符串并强制执行限制,否则无法指定允许的字符串大小的输入长度。与之相关的是额外的或不匹配的格式占位符,它们与实际的可变参数列表不匹配。这些占位符可能部分从堆栈中提取,包含不希望的甚至不安全的指针,具体取决于可变参数的特定实现。
/*另一种只在某些特殊编译器上有效的用法是
scanf("请输入一个值 %d",&n);
它会打印引号中的字符串,并在指示的%符号处停止接受输入。*/
另请参见
[edit | edit source]外部链接
[edit | edit source]- Linux 库函数 手册 : 输入格式转换 –
- C++ 对
std::scanf
的引用
- ↑ C99 标准,§7.19.6.2 “fscanf 函数” 第 11 行。