7

就本问题而言,CSV 样式的带引号的字符串是一个字符串,其中:

  1. 字符串正好以 1 开始和结束"
  2. 字符串中的两个双引号折叠成一个双引号。"Alo""ha"→<code>阿罗”哈。
  3. "" 本身是一个空字符串。
  4. "A""" e"无法解析错误输入,例如。它是一个A", 后面是 junk e"

我已经尝试了几件事,但都没有完全奏效。

得益于 Mozilla IRC 上#nom 用户 pinkieval 的帮助,我得到了最接近的结果:

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).

我已经确定Nom 1.0 的给定 CSV 示例不适用于我描述的带引号的 CSV 字符串,但我知道实现不同。

4

1 回答 1

2

这是一种方法:

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

(我是用全名写的,但是像你这样的解决方案,基于外部函数而不是map!()每个字符,也可以工作,并且可能更有效。)

这里的魔法,也可以解决你的正则表达式问题,是使用 CompleteStr。这基本上nom表明该输入之后不会有任何内容(否则,nom假设您正在执行流式解析器,因此可能会有更多输入)。

这是必需的,因为我们需要知道"如果它是最后一个输入的字符,该怎么处理nom。根据后面的字符(另一个",普通字符或 EOF),我们必须做出不同的决定——因此Incomplete结果,意思nom是没有足够的输入来做出决定。告诉nomEOF 接下来是解决这个犹豫不决的问题。

进一步阅读Incomplete作者nom的博客:http ://unhandledexpression.com/general/2018/05/14/nom-4-0-faster-safer-simpler-parsers.html#dealing-with-incomplete-usage


您可能会注意到,此解析器实际上并没有拒绝无效输入,而是解析开头并返回其余部分。如果您将此解析器用作另一个解析器中的子解析器,则后者会将剩余部分提供给下一个子解析器,这也会崩溃(因为它需要一个逗号),从而导致整个解析器失败。

如果你不想这样,你可以做csv_style_stringmatch peek!(alt!(char!(',')|char!('\n")|eof!()))

于 2018-06-09T23:17:27.730 回答