如何在nom中创建流式解析器?

时间:2017-10-22 17:06:08

标签: rust nom

我在nom中创建了一些非平凡的解析器,所以我现在对它非常熟悉。到目前为止,我创建的所有解析器始终将整个输入切片提供给解析器。

我想创建一个流解析器,我假设意味着我可以继续将字节输入解析器,直到它完成。我很难找到任何说明这一点的文档或示例,我也质疑我对“流解析器”的假设。

我的问题是:

  • 我对流解析器的正确理解是什么吗?
  • 如果是这样,有没有使用这种技术的解析器的好例子?

2 个答案:

答案 0 :(得分:2)

nom解析器既没有保留缓冲区来提供更多数据,也没有维护" state"以前他们需要更多的字节。

但是,如果您查看IResult结构,您会看到您可以返回部分结果或表明您需要更多数据。

似乎提供了一些处理流式传输的结构:我认为您应该使用consumer_from_parser!宏从解析器创建Consumer,为您的数据源实现Producer ,并调用run直到它返回None(并在有更多数据时再次启动)。到目前为止,示例和文档似乎大部分都缺失了 - 请参阅https://github.com/Geal/nom的底部:)

同样看起来nom中的大多数函数和宏都没有很好地(或根本没有)记录它们在输入结尾时的行为。例如,take_until!如果输入的长度不足以包含要查找的Incomplete,则会返回substr,但如果输入足够长但不会返回错误,则会返回错误。 t包含substr

同样nom主要使用&[u8]&str作为输入;你无法发出信号流的实际结果"通过这些类型。您可以实现自己的输入类型(相关特征:nom::{AsBytes,Compare,FindSubstring,FindToken,InputIter,InputLength,InputTake,Offset,ParseTo,Slice})以添加"到达流的末尾"标记,但nom提供的宏和函数无法解释它。

总而言之,我建议通过其他方式将流式输入拆分为可以使用简单的非流式解析器处理的块(甚至可以使用synom而不是nom)。

答案 1 :(得分:1)

这是一个最小的工作示例。正如@Stefan所说,“我建议通过其他方式将流输入拆分为可以处理的块”。

有些有用(我很乐意提出改进建议)是组合一个File::bytes()方法,然后仅根据需要添加take个字节,并将它们传递给{ {1}}。

nom::streaming::take

完整功能如下:

let reader = file.bytes();
let buf = reader.take(length).collect::<B>()?;
let (_input, chunk) = take(length)(&*buf)...; 

以下是与Unix的/// Parse the first handful of bytes and return the bytes interpreted as UTF8 fn parse_first_bytes(file: std::fs::File, length: usize) -> Result<String> { type B = std::result::Result<Vec<u8>, std::io::Error>; let reader = file.bytes(); let buf = reader.take(length).collect::<B>()?; let (_input, chunk) = take(length)(&*buf) .finish() .map_err(|nom::error::Error { input: _, code: _ }| eyre!("..."))?; let s = String::from_utf8_lossy(chunk); Ok(s.to_string()) } 命令类似的实现的主要内容。

head