第26.3章:scan

English

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 等

要点