std::scanf, std::fscanf, std::sscanf
在标头 <cstdio> 定义
|
||
int scanf( const char* format, ... ); |
(1) | |
int fscanf( std::FILE* stream, const char* format, ... ); |
(2) | |
int sscanf( const char* buffer, const char* format, ... ); |
(3) | |
从各种源读取数据,按照 format 进行判读并存储结果于给定位置。
参数
stream | - | 读取来源的文件流 |
buffer | - | 指向读取数据来源的空终止字符串 |
format | - | 指向空终止字符串的指针,指定如何读取输入 |
... | - | 接收的参数 |
格式字符串由下列内容组成
- 非空白多字节字符,除了
%
:每个格式字符串中的这种字符处理一个来自输入流的完全相同的字符,或若它与流的下个字符比较不相等则导致函数失败。 - 空白字符:任何格式字符串中的单个空白字符处理所有来自输入的可用连续空白字符(如同通过于循环中调用 isspace 确定)。注意,格式字符串中 "\n"、" "、"\t\t" 或其他空白无区别。
- 转换指示:每个转换指示拥有下列格式:
- 引入用 % 字符
- (可选)赋值抑制字符 *。若存在此选项,则此函数不将结果赋值给任何接收用实参。
- (可选)指定最大字段宽度的整数数字(大于零),即函数进行在当前转换指示所指定的转换时,允许处理的最大字符数。注意若不提供宽度,则 %s 和 %[ 可能导致缓冲区溢出。
- (可选)指定接收实参大小的长度修饰符,即实际目标类型。这影响转换准确性和溢出规则。默认目标类型对每个转换类型有所不同(见下表)。
- 转换格式指示符。
下列格式指示符可用:
转换 指示符 |
解释 | 实参类型 | ||||||||
---|---|---|---|---|---|---|---|---|---|---|
长度修饰符 →
|
hh
(C++11) |
h
|
(无) | l
|
ll
(C++11) |
j
(C++11) |
z
(C++11) |
t
(C++11) |
L
| |
%
|
匹配字面 % 。
|
不适用 | 不适用 | 不适用 | 不适用 | 不适用 | 不适用 | 不适用 | 不适用 | 不适用 |
c
|
若使用了宽度指示符,则匹配恰好宽度个字符(该实参必须是指向有充足空间的数组的指针)。不同于 %s 和 %[ ,它不会在数组后附加空字符。 |
不适用 | 不适用 | char* |
wchar_t* |
不适用 | 不适用 | 不适用 | 不适用 | 不适用 |
s
|
若使用宽度指示符,则至多匹配宽度个字符,或匹配到首个提前出现的空白符前。总是在匹配的字符后存储一个空字符(故实参数组必须有至少宽度 +1个字符的空间)。 | |||||||||
[ 集合]
|
若集合的首字符是 | |||||||||
d
|
该数的格式同 strtol() 以值 10 为 |
signed char* 或 unsigned char* |
signed short* 或 unsigned short* |
signed int* 或 unsigned int* |
signed long* 或 unsigned long* |
signed long long* 或 unsigned long long* |
intmax_t* 或 uintmax_t* |
size_t* |
ptrdiff_t* |
不适用 |
i
|
该数的格式同 strtol() 以值 0 为 | |||||||||
u
|
该数的格式同 strtoul() 以值 10 为 | |||||||||
o
|
该数的格式同 strtoul() 以值 8 为 | |||||||||
x , X
|
该数的格式同 strtoul() 以值 16 为 | |||||||||
n
|
不消耗输出。不增加赋值计数。若此指示符拥有赋值抑制运算符,则行为未定义。 | |||||||||
a , A (C++11)e , E f , F g , G
|
该数的格式同 strtof() 所期望者。 |
不适用 | 不适用 | float* |
double* |
不适用 | 不适用 | 不适用 | 不适用 | long double* |
p
|
|
不适用 | 不适用 | void** |
不适用 | 不适用 | 不适用 | 不适用 | 不适用 | 不适用 |
对于每个异于 n 的转换指示符,不超过任何指定字段宽度,且要么恰好是转换指示符所期待,要么是其所期待的前缀的最长输入字符序列,即是从流中消耗的内容。此消耗序列后的首个字符若存在,则保持未读取。若被消耗序列长度为零,或被消耗序列不能转换成上面所指定的项目,则发生匹配失败,除非遇到文件尾、编码错误,或阻止从流输入的读取错误,此情况下此为输入失败。
所有异于 [、c 和 n 的转换指示符,在尝试分析输入前消耗并舍弃所有前导空白字符(如同以调用 isspace 来确定)。这些被消耗的字符不计入指定的最大字段宽度。
转换指示符 lc、ls 和 l[ 进行多字节到宽字符转换,如同如同在转换首字符前,通过用初始化到零的 mbstate_t 对象调用 mbrtowc()。
转换指示符 s 与 [ 始终在匹配字符之后存储一个空字符。目标数组的大小必须至少比指定字段宽度大一。未指定目标数组大小时,对 %s 或 %[ 的使用,与 std::gets 同样不安全。
定宽整数类型(int8_t
等)的正确的转换指示定义于头文件 <cinttypes> (虽然 SCNdMAX、SCNuMAX 等就是 %jd、%ju 等的别名)。
在每个转换指示符后有一个序列点;这允许存储多个字段到同一“池”变量中。
在分析以无数字指数为结尾的不完整浮点值,如以转换指示符 %f 分析 "100er" 时,消耗序列 "100e" (可能为合法浮点数的最长前缀),并导致匹配错误(被消耗序列不能转换成浮点数),而留下 "r"。某些既存实现不遵守此规则并回滚,通过消耗 "100" 而留下 "er",例如 glibc 漏洞 1765。
返回值
成功赋值的接收实参个数(在首个接收实参被赋值前发生匹配失败的情况下可为零),或若在首个接收实参被赋值前发生输入失败则为 EOF。
复杂度
无保证。请注意,std::sscanf
的一些实现为 O(N),其中 N = std::strlen(buffer) [1]。对于高性能字符串分析,见 std::from_chars
。
注解
因为大多数转换指示符首先消耗所有连续空白符,如下代码
std::scanf("%d", &a); std::scanf("%d", &b);
会读取输入于不同行(第二个 %d 会消耗第一个剩下的换行符)或同一行中为空格或制表符所分隔的(第二个 %d 会消耗空格或制表符)二个整数。
不消耗前导空白符的转换指示符,如 %c,可通过在格式化字符串中用空白符使得它这么做:std::scanf("%d", &a); std::scanf(" %c", &c); // 忽略 %d 后的换行符,然后读一个 char
Note that some implementations of std::sscanf
involve a call to std::strlen, which makes their runtime linear on the length of the entire string. This means that if std::sscanf
is called in a loop to repeatedly parse values from the front of a string, your code might run in quadratic time (example).
示例
#include <clocale> #include <cstdio> #include <iostream> int main() { int i, j; float x, y; char str1[10], str2[4]; wchar_t warr[2]; std::setlocale(LC_ALL, "en_US.utf8"); char input[] = u8"25 54.32E-1 Thompson 56789 0123 56ß水"; // 按如下分析: // %d:整数 // %f:浮点值 // %9s:至多 9 个非空白字符的字符串 // %2d:二位整数(数字 5 与 6) // %f:浮点值(数字 7、8、9) // %*d 不存储于任何位置的整数 // ' ':所有连续空白符 // %3[0-9]:至多 3 位数字的字符串(数字 5 与 6) // %2lc:两个宽字符,用多字节到宽转换 const int ret = std::sscanf(input, "%d%f%9s%2d%f%*d %3[0-9]%2lc", &i, &x, str1, &j, &y, str2, warr); std::cout << "转换了 " << ret << " 个字段:\n" "i = " << i << "\n" "x = " << x << "\n" "str1 = " << str1 << "\n" "j = " << j << "\n" "y = " << y << "\n" "str2 = " << str2 << std::hex << "\n" "warr[0] = U+" << (int)warr[0] << "\n" "warr[1] = U+" << (int)warr[1] << '\n'; }
输出:
转换了 7 个字段: i = 25 x = 5.432 str1 = Thompson j = 56 y = 789 str2 = 56 warr[0] = U+df warr[1] = U+6c34
参阅
(C++11)(C++11)(C++11) |
使用可变实参列表 从 stdin、文件流或缓冲区读取有格式输入 (函数) |
从文件流获取字符串 (函数) | |
(C++11) |
打印有格式输出到 stdout、文件流或缓冲区 (函数) |
(C++17) |
转换字符序列到整数或浮点值 (函数) |