转义单引号的字符串解析

3
我想解析一个包含在单引号之间的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)
}

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

6
这有点棘手,但以下方法可行:
//# 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 而不是 &str。换言之,会有额外的分配。


谢谢!但是分配有点让我无法接受,因为我将要解析大量的文本,并希望它的性能尽可能好!=) 难道不能以某种方式使用 bytes::complete::escaped 解析器吗?这个 escaped(alphanumeric1, '\'', char('\''))(i) 不起作用... =( - undefined
@mottosson escapedescaped_transform具有不同的语义,前者仅检查输入是否符合给定的转义规则。将双引号替换为单引号需要操作可能很长的字符串的内部,我不知道如何避免分配内存。除非您可以以某种方式放宽该要求。 - undefined
1
@mottosson,那么您可以完全摆脱unquotemap_parserlet res = delimited(tag("'"), seq, tag("'"))(input)?;就足够了,当然返回类型将是IResult<&str, &str> - undefined
关于many0的另一个问题。文档中提到,many0会“重复嵌入的解析器直到失败,并将结果返回为Vec”。这是否意味着即使它被包裹在recognize中,它仍然会分配内存? - undefined
@mottosson 是的,应该可以。但即使如此,它也只是 Vec<&str>,不会占用太多内存。而且,在这里它被包裹在 recognize 中,所以结果从未绑定到任何变量或返回,应该立即丢弃。 - undefined
显示剩余3条评论

0
我正在学习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 版本。


谢谢,但是这在实际使用情况中不起作用。在这种情况下,单引号字符串是较大输入字符串的一部分,并且不以'结束。在最后一个'之后还有更多字符需要解析。这在我的问题中可能没有表达清楚。 - undefined

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接