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

标签 csv rust nom

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

  1. 字符串以一个 " 开头和结尾。
  2. 字符串中的两个双引号折叠成一个双引号。 “Alo”“ha”Alo“ha”
  3. ""本身是一个空字符串。
  4. 无法解析错误输入,例如 "A"""e"。这是一个 A",后跟垃圾 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)

我确定 given CSV example for Nom 1.0不适用于我描述的带引号的 CSV 字符串,但我知道实现方式不同。

最佳答案

这是一种实现方式:

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 不有足够的意见来做出决定。告诉 nom 接下来是 EOF 可以解决这种犹豫不决的问题。

进一步阅读 nom 作者博客上的 Incomplete: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 !())).

关于csv - 如何匹配 nom 中的 CSV 样式带引号的字符串?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50740577/

相关文章:

python - 用 Python 解析 90MB 的数据存档

Python 循环从 CSV 中的数据创建 MYSQL 插入语句

file - 使用 std::iter::Iterator 映射将文本文件行解析为数字

python - 如何使用 pyo3 从 Python 文件中调用 Rust 函数?

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

rust - 具有严格格式的可选字段

javascript - 解析 Highcharts 中多个类别的 CSV

postgresql - 使用 Rails、Postgres 和 Sidekiq 导入 CSV

rust - 我不安全的唯一选择是传递对此方法的引用以期望关闭吗?

rust - 如何使用 nom 吞噬字符串直到分隔符或结尾?