【问题标题】:Parse string with escaped single quotes用转义的单引号解析字符串
【发布时间】:2020-02-19 00:45:34
【问题描述】:

我想解析一个包含单引号之间的 ASCII 字符的字符串,并且该字符串可以包含连续两个 ' 的转义单引号。

'单引号之间包含的字符串值 -> '' 等等...'

这应该导致:

单引号之间的字符串值 -> ' 等等...

use nom::{
    bytes::complete::{tag, take_while},
    error::{ErrorKind, ParseError},
    sequence::delimited,
    IResult,
};

fn main() {
    let res = string_value::<(&str, ErrorKind)>("'abc''def'");

    assert_eq!(res, Ok(("", "abc\'def")));
}

pub fn is_ascii_char(chr: char) -> bool {
    chr.is_ascii()
}

fn string_value<'a, E: ParseError<&'a str>>(i: &'a str) -> IResult<&'a str, &'a str, E> {
    delimited(tag("'"), take_while(is_ascii_char), tag("'"))(i)
}

如何检测转义的引号而不是字符串的结尾?

【问题讨论】:

    标签: parsing rust nom


    【解决方案1】:

    这很棘手,但以下工作:

    //# nom = "5.0.1"
    use nom::{
        bytes::complete::{escaped_transform, tag},
        character::complete::none_of,
        combinator::{recognize, map_parser},
        multi::{many0, separated_list},
        sequence::delimited,
        IResult,
    };
    
    fn main() {
        let (_, res) = parse_quoted("'abc''def'").unwrap();
        assert_eq!(res, "abc'def");
        let (_, res) = parse_quoted("'xy@$%!z'").unwrap();
        assert_eq!(res, "xy@$%!z");
        let (_, res) = parse_quoted("'single quotes -> '' and so on...'").unwrap();
        assert_eq!(res, "single quotes -> ' and so on...");
    }
    
    fn parse_quoted(input: &str) -> IResult<&str, String> {
        let seq = recognize(separated_list(tag("''"), many0(none_of("'"))));
        let unquote = escaped_transform(none_of("'"), '\'', tag("'"));
        let res = delimited(tag("'"), map_parser(seq, unquote), tag("'"))(input)?;
    
        Ok(res)
    }
    

    一些解释:

    1. 解析器seq 可识别在双引号和其他任何字符之间交替出现的任何序列;
    2. unquote 将任何双引号转换为单引号;
    3. map_parser 然后将两者结合在一起以产生所需的结果。

    请注意,由于使用了escaped_transform 组合子,解析结果为String 而不是&amp;str。即,有额外的分配。

    【讨论】:

    • 谢谢!但是分配对我来说有点麻烦,因为我将解析大量文本并希望它尽可能高效! =) 不能以某种方式使用bytes::complete::escaped 解析器吗?这个escaped(alphanumeric1, '\'', char('\''))(i) 不起作用... =(
    • @mottosson escapedescaped_transform 语义不同,前者只检查输入是否符合给定的转义规则。用单引号替换双引号需要操作可能很长的字符串的内部,我不知道如何避免分配。除非你能以某种方式放宽这个要求。
    • 嗯...你是对的。也许可以在稍后阶段的单独步骤中进行转换,以规避最初的性能影响。那么我可以使用escaped 然后以与我之前的评论类似的方式使解析器了解如果下一个字符也是单引号,则字符串不会以它找到的第一个单引号结尾?
    • @mottosson 那么你可以完全摆脱unquotemap_parserlet res = delimited(tag("'"), seq, tag("'"))(input)?; 就足够了,当然返回类型将是 IResult&lt;&amp;str, &amp;str&gt;
    • 关于many0 的另一个问题。它在文档中说 many0 Repeats the embedded parser until it fails and returns the results in a Vec。这是否意味着即使包裹在recognize 中也会分配内存?
    【解决方案2】:

    我正在学习 nom,下面是我的尝试。

    let a = r###"'string value contained between single quotes -> '' and so on...'"###;
    
    fn parser(input: &str) -> IResult<&str, &str> {
        let len = input.chars().count() - 2;
        delimited(tag("'"), take(len), tag("'"))(input)
    }
    
    let (remaining, mut matched) = parser(a).unwrap_or_default();
    
    let sss = matched.replace("''", "'");
    matched = &sss;
    println!("remaining: {:#?}", remaining);
    println!("matched: {:#?}", matched);
    

    它打印这个结果:

    remaining: ""
    matched: "string value contained between single quotes -> ' and so on..."
    

    我的测试基于 nom 6.2.1。

    【讨论】:

    • 谢谢,但这在单引号字符串是较大输入字符串的一部分且不以 ' 结尾的实际用例中不起作用。在最后一个 '.这在我的问题中可能更清楚。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-01-10
    • 2019-08-23
    • 1970-01-01
    • 2016-07-02
    • 1970-01-01
    • 2023-03-03
    相关资源
    最近更新 更多