为什么我的 nom 解析器不消耗整个输入,而留下最后一块未解析?
Why does my nom parser not consume the entire input, leaving the last piece unparsed?
我试图在 space 和逗号上拆分日志行,以便创建 Field
和 [=16= 的 Token
的 Vector
] 如下代码所示。
我的问题是 nom 似乎没有消耗整个日志行,它留下了最后一部分未解析 - 在这种情况下 08:33:58)
.
main.rs
#![feature(rust_2018_preview)]
#[macro_use] extern crate nom;
#[derive(Debug, PartialEq)]
pub enum Token<'a> {
Separator(&'a [u8]),
Field(&'a [u8]),
}
named!(separator, is_a!(" ,"));
named!(not_sep, is_not!(" ,"));
named!(
token<Token>,
alt_complete!(
separator => { |s| Token::Separator(s) } |
not_sep => { |n| Token::Field(n) }
)
);
named!(sequence<Vec<Token>>, many1!(token));
pub fn scan(input: &[u8]) -> Vec<Token> {
let (_, seq) = sequence(input).unwrap();
seq
}
fn main() {
}
#[cfg(test)]
mod tests {
use std::str;
use crate::Token;
use crate::scan;
#[test]
fn parse_stuff() {
let log = &b"docker INFO 2019-10-01 08:33:58,878 [1] schedule:run Running job Every 1 hour do _precache_systems_streaks() (last run: 2018-09-21 07:33:58, next run: 2018-09-21 08:33:58)";
let seq = scan(&log[..]);
for t in seq {
let text = match t {
Token::Field(data) => format!("f[{}]", str::from_utf8(data).unwrap()),
Token::Separator(data) => format!("s[{}]", str::from_utf8(data).unwrap()),
};
println!("{}", text);
}
}
}
Cargo.toml
[dependencies]
nom = "4.0"
输出
f[docker]
s[ ]
f[INFO]
s[ ]
f[2019-10-01]
s[ ]
f[08:33:58]
s[,]
f[878]
s[ ]
f[[1]]
s[ ]
f[schedule:run]
s[ ]
f[Running]
s[ ]
f[job]
s[ ]
f[Every]
s[ ]
f[1]
s[ ]
f[hour]
s[ ]
f[do]
s[ ]
f[_precache_systems_streaks()]
s[ ]
f[(last]
s[ ]
f[run:]
s[ ]
f[2018-09-21]
s[ ]
f[07:33:58]
s[, ]
f[next]
s[ ]
f[run:]
s[ ]
f[2018-09-21]
s[ ]
您 运行 遇到的问题是,Nom 旨在始终假定可能有更多输入,除非您另有说明。由于您知道此处的输入是完整的,因此您需要向解析器提供包裹在 CompleteByteSlice
中的文字(或者如果您使用了 &str
,则为 CompleteStr
)。这些类型是 Nom 用来指示我们知道没有更多输入的薄包装器。它将使解析器无法完全匹配 returns 和 Error
而不是 Incomplete
,在这种情况下,将指示解析器使用该最终标记,而不是要求更多字符。
为了完整起见,我根据@Zarenor 的回答实施了以下更改,解析器现在消耗了整个输入。
更改为 main.rs
use nom::types::CompleteByteSlice;
use nom::IResult;
named!(separator<CompleteByteSlice, CompleteByteSlice>, is_a!(" ,"));
named!(not_separator<CompleteByteSlice, CompleteByteSlice>, is_not!(" ,"));
fn token<'a>(input: CompleteByteSlice<'a>) -> IResult<CompleteByteSlice<'a>, Token<'a>> {
alt!(input,
separator => { | s: CompleteByteSlice<'a> | Token::Separator(s.0) } |
not_separator => { | n: CompleteByteSlice<'a> | Token::Field(n.0) }
)
}
named!(sequence<CompleteByteSlice, Vec<Token>>, many1!(token));
pub fn scan(input: &[u8]) -> Vec<Token> {
let (_, seq) = sequence(CompleteByteSlice(input)).unwrap();
seq
}
我试图在 space 和逗号上拆分日志行,以便创建 Field
和 [=16= 的 Token
的 Vector
] 如下代码所示。
我的问题是 nom 似乎没有消耗整个日志行,它留下了最后一部分未解析 - 在这种情况下 08:33:58)
.
main.rs
#![feature(rust_2018_preview)]
#[macro_use] extern crate nom;
#[derive(Debug, PartialEq)]
pub enum Token<'a> {
Separator(&'a [u8]),
Field(&'a [u8]),
}
named!(separator, is_a!(" ,"));
named!(not_sep, is_not!(" ,"));
named!(
token<Token>,
alt_complete!(
separator => { |s| Token::Separator(s) } |
not_sep => { |n| Token::Field(n) }
)
);
named!(sequence<Vec<Token>>, many1!(token));
pub fn scan(input: &[u8]) -> Vec<Token> {
let (_, seq) = sequence(input).unwrap();
seq
}
fn main() {
}
#[cfg(test)]
mod tests {
use std::str;
use crate::Token;
use crate::scan;
#[test]
fn parse_stuff() {
let log = &b"docker INFO 2019-10-01 08:33:58,878 [1] schedule:run Running job Every 1 hour do _precache_systems_streaks() (last run: 2018-09-21 07:33:58, next run: 2018-09-21 08:33:58)";
let seq = scan(&log[..]);
for t in seq {
let text = match t {
Token::Field(data) => format!("f[{}]", str::from_utf8(data).unwrap()),
Token::Separator(data) => format!("s[{}]", str::from_utf8(data).unwrap()),
};
println!("{}", text);
}
}
}
Cargo.toml
[dependencies]
nom = "4.0"
输出
f[docker]
s[ ]
f[INFO]
s[ ]
f[2019-10-01]
s[ ]
f[08:33:58]
s[,]
f[878]
s[ ]
f[[1]]
s[ ]
f[schedule:run]
s[ ]
f[Running]
s[ ]
f[job]
s[ ]
f[Every]
s[ ]
f[1]
s[ ]
f[hour]
s[ ]
f[do]
s[ ]
f[_precache_systems_streaks()]
s[ ]
f[(last]
s[ ]
f[run:]
s[ ]
f[2018-09-21]
s[ ]
f[07:33:58]
s[, ]
f[next]
s[ ]
f[run:]
s[ ]
f[2018-09-21]
s[ ]
您 运行 遇到的问题是,Nom 旨在始终假定可能有更多输入,除非您另有说明。由于您知道此处的输入是完整的,因此您需要向解析器提供包裹在 CompleteByteSlice
中的文字(或者如果您使用了 &str
,则为 CompleteStr
)。这些类型是 Nom 用来指示我们知道没有更多输入的薄包装器。它将使解析器无法完全匹配 returns 和 Error
而不是 Incomplete
,在这种情况下,将指示解析器使用该最终标记,而不是要求更多字符。
为了完整起见,我根据@Zarenor 的回答实施了以下更改,解析器现在消耗了整个输入。
更改为 main.rs
use nom::types::CompleteByteSlice;
use nom::IResult;
named!(separator<CompleteByteSlice, CompleteByteSlice>, is_a!(" ,"));
named!(not_separator<CompleteByteSlice, CompleteByteSlice>, is_not!(" ,"));
fn token<'a>(input: CompleteByteSlice<'a>) -> IResult<CompleteByteSlice<'a>, Token<'a>> {
alt!(input,
separator => { | s: CompleteByteSlice<'a> | Token::Separator(s.0) } |
not_separator => { | n: CompleteByteSlice<'a> | Token::Field(n.0) }
)
}
named!(sequence<CompleteByteSlice, Vec<Token>>, many1!(token));
pub fn scan(input: &[u8]) -> Vec<Token> {
let (_, seq) = sequence(CompleteByteSlice(input)).unwrap();
seq
}