【问题标题】:Why does my nom parser not consume the entire input, leaving the last piece unparsed?为什么我的 nom 解析器不消耗整个输入,而留下最后一块未解析?
【发布时间】:2019-03-14 04:12:55
【问题描述】:

我正在尝试在空格和逗号上拆分日志行,以便创建Tokens 的FieldSeparatorVector,如下面的代码所示。

我的问题是 nom 似乎没有消耗整个日志行,它使最后一部分未解析 - 在本例中为 08:33:58)

ma​​in.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[ ]

【问题讨论】:

标签: rust nom


【解决方案1】:

您遇到的问题是,Nom 旨在始终假设可能有更多输入,除非您另有说明。由于您知道您在此处的输入是完整的,因此您需要将包裹在 CompleteByteSlice 中的文字提供给解析器(或者如果您使用了 &amp;str,则为 CompleteStr)。这些类型是 Nom 用来指示我们知道没有更多输入的薄包装器。它将使它成为一个无法进行完全匹配的解析器返回Error 而不是Incomplete,并且在这种情况下,将指示解析器使用最终令牌,而不是要求更多字符。

【讨论】:

    【解决方案2】:

    为了完整起见,我根据@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
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-08-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多