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 解析器需要避免引号中的逗号的主要内容,如果未能解决你的问题,请参考以下文章