代码之家  ›  专栏  ›  技术社区  ›  mchlstckl

为什么我的nom解析器不使用整个输入,而留下最后一个未分析的部分?

  •  5
  • mchlstckl  · 技术社区  · 7 年前

    我试着在空格和逗号上拆分一个日志行,以便创建一个 Vector 属于 Token Field Separator 如下面的代码所示。

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

    主要风险

    #![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);
            }
        }
    }
    

    货物.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[ ]
    
    2 回复  |  直到 7 年前
        1
  •  6
  •   Zarenor    7 年前

    CompleteByteSlice (或者如果您使用 &str CompleteStr ). 这些类型是瘦包装器,Nom使用它们来表示我们知道不会有更多的输入。它将使未能进行完全匹配的解析器返回 Error 而不是 Incomplete

        2
  •  1
  •   mchlstckl    7 年前

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