nom解析器既不维护一个缓冲区来提供更多的数据,也不维护以前需要更多字节的“状态”。 但是如果你看一下IResult结构,你会发现你可以返回一个部分的结果或者指出你需要更多的数据。 似乎提供了一些结构来处理流:我认为你应该使用consumer_from_parser!宏从解析器创建一个Consumer,为你的数据源实现一个Producer,然后调用run直到它返回None(当你有更多的数据时再开始)。到目前为止,示例和文档似乎大部分都丢失了-参见https://github.com/Geal/nom的底部:) 此外,看起来nom中的大多数函数和宏在到达输入结束时的行为都没有很好的文档记录(或根本没有)。例如,如果输入的长度不足以包含要查找的substr,则take_until!返回Incomplete,但如果输入的长度足够长但不包含substr,则返回错误。 另外,nom主要使用&[u8]或&str作为输入;你不能通过这些类型发出实际的“流结束”信号。您可以实现自己的输入类型(相关traits:nom::{AsBytes,Compare,FindSubstring,FindToken,InputIter,InputLength,InputTake,Offset,ParseTo,Slice})添加一个“reached end of stream”标志,但是nom提供的宏和函数将无法解释它。 总而言之,我建议通过一些其他方式将流输入拆分为可以使用简单的非流解析器处理的块(甚至可以使用synom而不是nom)。
let reader = file.bytes();
let buf = reader.take(length).collect::<B>()?;
let (_input, chunk) = take(length)(&*buf)...;
完整的函数可以如下所示:
/// 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())
}
下面是main的其余部分,用于类似于Unix的head命令的实现。
use color_eyre::Result;
use eyre::eyre;
use nom::{bytes::streaming::take, Finish};
use std::{fs::File, io::Read, path::PathBuf};
use structopt::StructOpt;
#[derive(Debug, StructOpt)]
#[structopt(about = "A minimal example of parsing a file only partially.
This implements the POSIX 'head' utility.")]
struct Args {
/// Input File
#[structopt(parse(from_os_str))]
input: PathBuf,
/// Number of bytes to consume
#[structopt(short = "c", default_value = "32")]
num_bytes: usize,
}
fn main() -> Result<()> {
let args = Args::from_args();
let file = File::open(args.input)?;
let head = parse_first_bytes(file, args.num_bytes)?;
println!("{}", head);
Ok(())
}
3条答案
按热度按时间oxalkeyp1#
nom
解析器既不维护一个缓冲区来提供更多的数据,也不维护以前需要更多字节的“状态”。但是如果你看一下
IResult
结构,你会发现你可以返回一个部分的结果或者指出你需要更多的数据。似乎提供了一些结构来处理流:我认为你应该使用
consumer_from_parser!
宏从解析器创建一个Consumer
,为你的数据源实现一个Producer
,然后调用run
直到它返回None
(当你有更多的数据时再开始)。到目前为止,示例和文档似乎大部分都丢失了-参见https://github.com/Geal/nom的底部:)此外,看起来
nom
中的大多数函数和宏在到达输入结束时的行为都没有很好的文档记录(或根本没有)。例如,如果输入的长度不足以包含要查找的substr
,则take_until!
返回Incomplete
,但如果输入的长度足够长但不包含substr
,则返回错误。另外,
nom
主要使用&[u8]
或&str
作为输入;你不能通过这些类型发出实际的“流结束”信号。您可以实现自己的输入类型(相关traits:nom::{AsBytes,Compare,FindSubstring,FindToken,InputIter,InputLength,InputTake,Offset,ParseTo,Slice}
)添加一个“reached end of stream”标志,但是nom
提供的宏和函数将无法解释它。总而言之,我建议通过一些其他方式将流输入拆分为可以使用简单的非流解析器处理的块(甚至可以使用
synom
而不是nom
)。huwehgph2#
下面是一个最小的工作示例。正如@Stefan所写的,“我建议通过其他方式将流输入拆分成你可以处理的块”。
有什么工作(我很高兴关于如何改进它的建议),是合并一个
File::bytes()
方法,然后只take
所需的字节数,并将它们传递给nom::streaming::take
。完整的函数可以如下所示:
下面是main的其余部分,用于类似于Unix的
head
命令的实现。h43kikqp3#
据我所知,
nom
的架构自从最初提出这个问题以来已经发生了变化(这就是为什么接受的答案对我来说不起作用)。我也在为同样的问题挣扎,我发现的越多,我就越意识到这不是那么简单和直接的。
我写了a blog post关于我的发现。简而言之,它可以归结为以下几个步骤:
next()
函数中,首先查看解析器是否会根据当前未解析的数据进行解析。如果它返回一个Err(Err::Incomplete(_))
,添加更多的数据并重试,直到它返回一个在迭代器中返回的对象。请参阅the blog post和this GitHub repo了解更多信息。