第26.3章:scan
1. 函数表面
输入有两个入口。scan 是全有或报错的形式;scan_some
是允许部分成功的形式。
| 函数 | 返回 | 输入结束时 |
|---|---|---|
scan(in, args...) |
void |
抛出携带 parse_errc::end_of_file 的 herbception。 |
scan_some(in, args...) |
scan_some_result_t |
返回一个结果,remained_args 表示还有多少个参数没被扫描。
只有全部参数都扫描成功时结果才为真。 |
两种形式都先接收输入设备。没有格式串。每个参数按自己的类型解析, scan 操纵器可以改变单个参数的语法。
#include <fast_io.h>
int main()
{
using namespace ::fast_io::io;
::fast_io::ibuf_file in("numbers.txt");
int a{};
int b{};
scan(in, a, b);
println("a + b = ", a + b);
}
2. scan 需要带缓冲的输入
格式化输入需要向前看:它必须看到下一个字符,才能知道当前 token 是否结束,
并且可能需要把这个字符留给下一个参数。所以 scan 要求输入设备有缓冲区。
用 ibuf_file 或 iobuf_file:
| 设备 | 能 scan 吗 | 原因 |
|---|---|---|
::fast_io::ibuf_file |
能 | 带缓冲输入;提供向前看和重新填充路径。 |
::fast_io::iobuf_file |
能 | 带缓冲双向设备;输入侧可用。 |
::fast_io::native_file |
不能 | 不带缓冲;没有可供向前看的输入缓冲区。包一层,或改用带缓冲文件类型。 |
::fast_io::iobuf_pipe |
能 | 带缓冲管道;生产者是另一个进程时很有用。 |
这是输出的镜像。print 对带缓冲和不带缓冲输出都成立,
因为它从不需要向前看;scan 没有输入缓冲区就无法工作,
类型系统会拒绝这种尝试,而不是在背后偷偷一个字节一个字节地读。
3. 失败:全有或全无 vs 部分
scan 用于必须完整的记录。如果输入在所有参数读完之前结束,它就抛出。
对配置行、命令记录或固定头部,这是正确的默认行为。
scan_some 用于“最后一条记录不完整是正常现象”的流。
它的结果携带没有被扫描的参数数量:
#include <fast_io.h>
int main()
{
using namespace ::fast_io::io;
::fast_io::ibuf_file in("records.txt");
int a{};
int b{};
auto result{scan_some(in, a, b)};
if (!result)
{
println("stopped with ", result.remained_args, " argument(s) left");
}
}
格式错误的 token 仍然是解析错误,仍然抛出。scan_some 只改变
“流在记录完成之前结束”的处理方式;它不会把非法输入变成成功。
4. 空白、进制与 token 边界
默认情况下,格式化输入跳过前导空白,读取能构成合法值的最长 token。 整数在第一个无法继续的字符处停止。字符串读取以空白分隔的 token, 除非用整行或字符串视图操纵器包住。
int x{};
::fast_io::io::scan(in, x); // decimal by default
::fast_io::io::scan(in, ::fast_io::mnp::hex_get(x));
hex_get、base_get<N>、oct_get、
bin_get、dec_get、addrvw_get、
ch_get、strlike_get、strlike_line_get 和
strlike_whole_get 是 scan 侧的操纵器。它们目前属于头文件辅助;
模块导出列表还没有包含它们。使用模块又需要它们时,包含对应的 fast_io 头文件,
或者直接用普通类型。
不要把 mnp::hex 这类格式化操纵器当作 scan 目标。
输出操纵器描述“怎么写一个值”,scan 操纵器描述“怎么读一个值”。
名字不同正是因为这个原因。
5. 能扫描什么
内置路径覆盖整数、浮点数、字符、布尔、字符串和字符串类类型、IP 地址、时间戳,
以及提供自己的 scan_context_define / scan_contiguous_define
定制的类型。没有 scan 支持的类型是编译期错误。
扫描进固定大小缓冲区是刻意不静默的。如果输入 token 比目标长,操作失败而不是截断。 用会增长的字符串类型,或者在协议规定长度时使用长度前缀/整行操纵器。
6. scan 与 print 对称,但不相同
| 性质 | print |
scan |
|---|---|---|
| 需要缓冲设备 | 否 | 输入侧需要 |
| 默认流 | C stdout |
没有默认;输入设备必须显式给出 |
| 失败 | 来自设备层的 herbception | 来自解析或设备层的 herbception |
| 部分操作 | 不适用;每次写要么完整要么失败 | scan_some 报告剩余参数 |
| 操纵器方向 | hex、fixed、left 等 |
hex_get、base_get<N>、ch_get 等 |
要点
scan是全有或报错;scan_some报告剩余了多少个参数。scan需要带缓冲的输入设备;native_file不是。- 两种形式都把输入设备作为第一个参数,都没有格式串。
scan_some处理短的最后一条记录,不处理非法输入;解析错误仍然抛出。- scan 语法用
*_get家族,不要用输出操纵器。 - 固定大小目标拒绝超长 token,而不是截断。