使用 nom 5.0 解析数字

Parsing number with nom 5.0

我正在尝试使用 Nom 5.0 解析大文件(数十 GB)流式传输。解析器的一部分尝试解析数字:

use nom::IResult;
use nom::character::streaming::{char, digit1};
// use nom::character::complete::{char, digit1};
use nom::combinator::{map, opt};
use nom::multi::many1;
use nom::sequence::{preceded, tuple};

pub fn number(input: &str) -> IResult<&str, &str> {
    map(
        tuple((
            opt(char('-')),
            many1(digit1),
            opt(preceded(char('.'), many1(digit1)))
        )),
        |_| "0"
    )(input)
}

(显然,它不应该 return 所有数字都为“0”;这只是为了使函数尽可能简单。)对于这个解析器,我写了一个测试:

#[test]
fn match_positive_integer() {
    let (_, res) = number("0").unwrap();
    assert_eq!("0", res);
}

此测试失败并返回 Incomplete(Size(1)),因为 "decimals" opt() 想要读取数据但不存在。如果我切换到 complete 版本的匹配器(如注释掉的行),则测试通过。

我认为这实际上会在生产中起作用,因为当抱怨不完整时它会被提供额外的数据,但我仍然想创建单元测试。此外,如果一个数字恰好是文件中输入的最后一位,那么在生产中就会出现这个问题。我如何说服流式 Nom 解析器没有更多可用数据?

可以说测试的原始形式是正确的:解析器无法确定给定的输入是否为数字,因此解析结果实际上尚未确定。在生产中,尤其是像您一样读取大文件时,已读取但待解析字节的缓冲区可能恰好在 可能 之间结束,除非它实际上是不是。然后,解析器需要保留其当前状态并请求更多输入以便它可以retry/continue。不要将 Incomplete 视为最终错误,而是将其视为 I don't even know: This could be an error depending on the next byte, this problem is undecidable as of yet!.

您可以在您的顶级解析器 上使用 complete-combinator ,所以当您实际上达到 EOF 时,您就会出错。 Incomplete-results within 应该处理顶级解析器,例如通过将读取缓冲区扩大一定幅度并重试。

您可以将解析器包装在当前单元测试的本地 complete()-解析器中并对其进行测试。有点像

#[test]
fn match_positive_integer() {
    let (_, res) = complete(number("0")).unwrap();
    assert_eq!("0", res);
}