Rust - 如何解析 nom 中的 UTF-8 字母字符?

Rust - How to parse UTF-8 alphabetical characters in nom?

我正在尝试解析字母字符的字符序列,包括德语变音符号 (ä ö ü) 和 UTF-8 字符集中的其他字母字符。 这是我首先尝试的解析器:

named!(
    parse(&'a str) -> Self,
    map!(
        alpha1,
        |s| Self { chars: s.into() }
    )
);

但它只适用于 ASCII 字母字符 (a-zA-Z)。 我试图通过 char:

执行解析 char
named!(
    parse(&str) -> Self,
    map!(
        take_while1!(nom::AsChar::is_alpha),
        |s| Self { chars: s.into() }
    )
);

但这甚至不会解析 "hello",而是导致 Incomplete(Size(1)) 错误:

如何解析 nom 中的 UTF-8 字母字符? 我的代码片段:

extern crate nom;

#[derive(PartialEq, Debug, Eq, Clone, Hash, Ord, PartialOrd)]
pub struct Word {
    chars: String,
}

impl From<&str> for Word {
    fn from(s: &str) -> Self {
        Self {
            chars: s.into(),
        }
    }
}

use nom::*;
impl Word {
    named!(
        parse(&str) -> Self,
        map!(
            take_while1!(nom::AsChar::is_alpha),
            |s| Self { chars: s.into() }
        )
    );
}


#[test]
fn parse_word() {
    let words = vec![
        "hello",
        "Hi",
        "aha",
        "Mathematik",
        "mathematical",
        "erfüllen"
    ];
    for word in words {
        assert_eq!(Word::parse(word).unwrap().1, Word::from(word));
    }
}

当我运行这个测试时,

cargo test parse_word

我得到:

thread panicked at 'called `Result::unwrap()` on an `Err` value: Incomplete(Size(1))', ...

我知道 chars 已经是用 Rust 编码的 UTF-8(谢天谢地,万能的),但似乎 nom 库的行为并不像我预期的那样。我正在使用 nom 5.1.0

首先nom 5使用函数进行解析,我建议使用这种形式,因为错误消息更好,代码更清晰。

你的要求很奇怪,你可以把完整的输入变成一个字符串:

impl Word {
    fn parse(input: &str) -> IResult<&str, Self> {
        Ok((
            &input[input.len()..],
            Self {
                chars: input.to_string(),
            },
        ))
    }
}

但我猜你的目的是解析一个词,所以这里有一个你可以做什么的例子:

#[derive(PartialEq, Debug, Eq, Clone, Hash, Ord, PartialOrd)]
pub struct Word {
    chars: String,
}

impl From<&str> for Word {
    fn from(s: &str) -> Self {
        Self { chars: s.into() }
    }
}

use nom::{character::complete::*, combinator::*, multi::*, sequence::*, IResult};

impl Word {
    fn parse(input: &str) -> IResult<&str, Self> {
        let (input, word) =
            delimited(space0, recognize(many1_count(none_of(" \t"))), space0)(input)?;
        Ok((
            input,
            Self {
                chars: word.to_string(),
            },
        ))
    }
}

#[test]
fn parse_word() {
    let words = vec![
        "hello",
        " Hi",
        "aha ",
        " Mathematik ",
        "  mathematical",
        "erfüllen ",
    ];
    for word in words {
        assert_eq!(Word::parse(word).unwrap().1, Word::from(word.trim()));
    }
}

您也可以制作一个使用 is_alphabetic() 而不是 none_of(" \t") 的自定义函数,但这需要为 nom 制作一个自定义错误,目前我认为这样做非常烦人。

在这个 Github Issue a fellow contributor quickly whipped up a library (nom-unicode) 上很好地处理这个问题:

use nom_unicode::complete::{alphanumeric1};

impl Word {
    named!(
        parse(&'a str) -> Self,
        map!(
            alphanumeric1,
            |w| Self::new(w)
        )
    );
}