如何在nom中匹配CSV样式的带引号的字符串?

bvjveswy  于 2023-09-27  发布在  其他
关注(0)|答案(2)|浏览(94)

对于本问题,CSV样式的带引号的字符串是这样的字符串:
1.字符串的开始和结束都恰好是一个"
1.字符串中的两个双引号折叠为一个双引号。"Alo""ha"Alo"ha
1.“”本身是一个空字符串。
1.错误输入(如"A""" e")无法解析。它是一个A",然后是垃圾e"
我试过几种方法,没有一种能完全奏效。
我得到的最接近的,感谢Mozilla IRC上用户pinkieval在#nom中的一些帮助:

use std::error as stderror; /* Avoids needing nightly to compile */

named!(csv_style_string<&str, String>, map_res!(
   terminated!(tag!("\""), not!(peek!(char!('"')))),
   csv_string_to_string
));

fn csv_string_to_string(s: &str) -> Result<String, Box<stderror::Error>> {
   Ok(s.to_string().replace("\"\"", "\""))
}

这不会正确地捕获字符串的结尾。
我还尝试将re_match!宏与r#""([^"]|"")*""#一起使用,但结果总是Err::Incomplete(1)
我已经确定given CSV example for Nom 1.0不适用于我所描述的带引号的CSV字符串,但我知道实现不同。

gstyhher

gstyhher1#

这里有一种方法:

use nom::types::CompleteStr;

use nom::*;

named!(csv_style_string<CompleteStr, String>,
    delimited!(
        char!('"'),
        map!(
            many0!(
                alt!(
                    // Eat a " delimiter and  the " that follows it
                    tag!("\"\"") => { |_| '"' }

                |    // Normal character
                    none_of!("\"")
                )
            ),
             // Make a string from a vector of chars
            |v| v.iter().collect::<String>()
        ),
        char!('"')
    )
);

fn main() {
    println!(r#""Alo\"ha" = {:?}"#, csv_style_string(CompleteStr(r#""Alo""ha""#)));
    println!(r#""" = {:?}"#, csv_style_string(CompleteStr(r#""""#)));
    println!(r#"bad format: {:?}"#, csv_style_string(CompleteStr(r#""A""" e""#)));
}

(我用完整的nom写的,但是像你这样的解决方案,基于一个外部函数而不是map!()每个字符,也可以工作,而且可能更有效。
这里的神奇之处是使用CompleteStr,它也可以解决您的regexp问题。这基本上告诉nom在输入之后不会有任何东西(否则,nom假设您正在进行流解析器,因此可能会有更多输入)。
这是必要的,因为我们需要知道如果"是输入到nom的最后一个字符,该如何处理它。根据它后面的字符(另一个",普通字符或EOF),我们必须做出不同的决定-因此Incomplete结果,这意味着nom没有足够的输入来做出决定。告诉nom下一个是nom就解决了这个犹豫不决的问题。
有关Incomplete的进一步阅读,请访问nom作者的博客:http://unhandledexpression.com/general/2018/05/14/nom-4-0-faster-safer-simpler-parsers.html#dealing-with-incomplete-usage
您可能会注意到,这个解析器实际上并不拒绝无效的输入,而是解析开头并返回其余部分。如果您将这个解析器用作另一个解析器中的子解析器,那么后者将把剩余部分提供给下一个子解析器,这也会崩溃(因为它需要一个逗号),导致整个解析器失败。
如果你不想这样,你可以让csv_style_string匹配peek!(alt!(char!(',')|char!('\n")|eof!()))

sycxhyv7

sycxhyv72#

另一个答案是nom4的宏风格。这是新的函数样式。

fn csv_style_string(input: &str) -> IResult<&str, String> {
    delimited(
        char('"'),
        map(
            many0(
                alt((
                    // Eat a " delimiter and the " that follows it
                    value('"', tag("\"\"")),

                    // Normal character
                    none_of("\""),
                )),
            ),
            // Make a string from a vector of chars
            |v| v.iter().collect::<String>(),
        ),
        char('"'),
    )(input)
}

相关问题