2

我正在尝试使用 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)
}

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

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

该测试失败,Incomplete(Size(1))因为“小数”opt()想要读取数据并且它不存在。如果我切换到complete匹配器的版本(如注释掉的行),则测试通过。

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

4

1 回答 1

0

有人可以争辩说原始形式的测试是正确的:解析器无法确定给定的输入是否是数字,因此解析结果实际上尚未确定。在生产中,尤其是在读取大文件时,已经读取但要解析的字节的缓冲区可能会在可能是数字之间结束,除非它实际上不是。然后,解析器需要保留其当前状态并要求更多输入,以便它可以重试/继续。不要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 ,因此当您实际上达到时,您会出错。- 顶级解析器中的结果应该被处理,例如通过将读取缓冲区扩展一些边距并重试EOFIncomplete

您可以将解析器包装在complete()当前单元测试本地的 -parser 中并对其进行测试。一些合乎情调的东西

#[test]
fn match_positive_integer() {
    let (_, res) = complete(number("0")).unwrap();
    assert_eq!("0", res);
}
于 2019-09-16T18:38:55.687 回答