Clojure 中的 CSV 解析器需要避免引号中的逗号

Posted

技术标签:

【中文标题】Clojure 中的 CSV 解析器需要避免引号中的逗号【英文标题】:CSV parser in Clojure needs to avoid commas in quotes 【发布时间】:2015-12-26 20:45:29 【问题描述】:

更新:通过这个:

#",(?=([^\"]*\"[^\"]*\")*[^\"]*$)"

clojure.string/split 解析CSV。

更新:我需要一个匹配所有不在引号中的逗号的正则表达式,其格式可供clojure.string/split 使用。

我在 Clojure 中写了一个 CSV 解析函数:

(defn parse-csv [data schema]
  (let [split-data (clojure.string/split data #",")]
    (loop [rm-data split-data
           rm-keys (:keys schema)
           rm-trans (:trans schema)
           final ]
      (if (empty? rm-keys)
           final
          (recur (rest rm-data)
                 (rest rm-keys)
                 (rest rm-trans)
                 (into final
                   (first rm-keys)
                   ((first rm-trans) (first rm-data))))))))

schema 只是一个哈希映射,由关键字列表和函数列表(应用于它们各自的值)组成。这用于定义输出哈希映射的外观。 这是一个例子:

(def schema :keys [:foo :bar :baz] :trans [identity read-string identity])
(parse-csv "Hello,42,world" schema) ;; returns :foo "Hello", :bar 42, :baz "world"

但是,如果我们这样做:

(def schema :keys [:foo :bar :baz] :trans [identity identity identity])
(parse-csv "Hello,\"Newell, Gabe\",world" schema) ;; returns :foo "Hello" :bar "\"Newell" :baz "Gabe\""

事情变得一团糟,“世界”这个词被忽略了。结果应该如下所示:

:foo "Hello" :bar "\"Newell, Gabe\"" :baz "world"

上述数据,在一个文件中,实际上看起来像Hello,"Newell, Gabe",world,所以我们需要避免在遇到"Newell, Gabe"中的逗号时触发split函数。

我们需要一个函数,将字符串按特定字符分割除非特定字符在引号中。

【问题讨论】:

为了在带引号的字符串中允许逗号,您需要先拆分字符串,然后用剩下的内容创建字段。或者使用真正的解析器。 github.com/clojure/data.csv 使用默认选项正确读取:(require '[clojure.data.csv :as csv]) (csv/read-csv "Hello,\"Newell, Gabe\",world") ; => (["Hello" "Newell, Gabe" "world"]) 【参考方案1】:

要允许在字段中使用分隔符(此处为 ,),需要引用这些字段。

您在这里使用\" 所做的是转义引号(unix 样式,引号也可以用另一个引号转义)。

因此,允许在字段内使用,

"Hello,"Newell, Gabe",world"

那些外部引号当然不应该是 csv 的一部分。

更新问题编辑后:

文件中的上述数据实际上看起来像 Hello,"Newell, Gabe",world,所以我们需要避免触发split函数 在“Newell, Gabe”中遇到逗号。

Hello,"Newell, Gabe",world

这是完全有效的 csv,但如果在 , 上使用纯拆分函数处理它,您就有麻烦了。

一个选项可能是使用另一个分隔符,例如 |;

更新 2

所以我需要一个将 CSV 字符串拆分为字段的新函数,除非 逗号用引号引起来(我无法更改数据集)。我该怎么走 关于实现这个?

对于每一行,您需要扫描每个字符,有点像伪代码中的这样(我不知道 Clojure,所以我无法为您提供一些代码,但我经常处理大型 csv 文件):

bool InsideQuotes = false;
loop through chars
  if `,` and InsideQuotes == false -> new field
  if `"` -> InsideQuotes = !InsideQuotes

这样,引用字段中的引号可以用另一个引号转义。例如:

Hello,"17"" monitor, Samsung",world

更新

对于 csv 上的一些 Regex,请参阅 this 和 this。

【讨论】:

文件中的数据实际上看起来像Hello,"Newell, Gabe",world,所以我们需要避免在遇到"Newell, Gabe"中的逗号时触发split函数。 是的,应该这样做。 好的,您的评论(和问题)已更改 - 请参阅答案中的更新。 更新答案后:我在别人的(多兆字节)数据集上使用我的 CSV“阅读器”,所以我需要一个新函数将 CSV 字符串拆分为字段除非 i> 逗号用引号引起来(我无法更改数据集)。我将如何实施? 哦,对不起 - 不想冒犯你!我认为 csv 解析器是 Clojure 的一部分。标题说:“Clojure CSV 解析器”。我会再更新一些答案。

以上是关于Clojure 中的 CSV 解析器需要避免引号中的逗号的主要内容,如果未能解决你的问题,请参考以下文章

在某些情况下使用双引号解析 CSV

Boost tokenizer 无法解析具有双引号字段的 csv 文件

正确转义 CSV 中的双引号

CSV 文件中的 ASP.NET 智能引号解析问题

正则表达式在csv中找到缺少的双引号

Access 解析 CSV 文件中的双引号的问题