使用clojure-csv.core解析巨大的csv文件
到目前为止,我已经:使用clojure-csv.core解析巨大的csv文件,csv,clojure,bigdata,Csv,Clojure,Bigdata,到目前为止,我已经: (:require [clojure-csv.core :as csv]) (:require [clojure.java.io :as io])) (def csv-file (.getFile (clojure.java.io/resource "verbs.csv"))) (defn process-csv [file] (with-open [rdr (io/reader file)] (csv/parse-csv rdr))) 但是我得到了
(:require [clojure-csv.core :as csv])
(:require [clojure.java.io :as io]))
(def csv-file (.getFile (clojure.java.io/resource "verbs.csv")))
(defn process-csv [file]
(with-open [rdr (io/reader file)]
(csv/parse-csv rdr)))
但是我得到了java.io.IOException:streamclosed
。我正在使用并且它公开了两种方法,第一种是我正在使用的,parse csv
,文档说:
Takes a CSV as a char sequence or string, and returns a lazy sequence of vectors of strings
我想我知道:的open
是懒惰的,而(csv/parse csv-rdr)中的rdr
)
是csv文件的一行,对吗
另外,我还想搜索该文件,重复打开该文件(即使该文件被惰性地读取)并搜索整个内容是否代价高昂???看起来,当该文件已关闭时,该文件正试图在
打开的表单外惰性地解析
尝试以下方法进行验证,打印前5行已解析的行:
(defn process-csv [file]
(with-open [rdr (io/reader file)]
(let [lines (csv/parse-csv rdr)]
(doseq [l (take 5 lines)]
(println l)))))
我认为,如果文件很大,那么多次打开该文件与搜索内部内容相比并不昂贵
如果你需要做很多次,我会考虑建立某种搜索索引。
<代码>打开< /COD>不是懒惰的,但是如果你在打开代码< >代码>中做了一个懒惰的事情,如果懒惰的动作没有被强制在<代码>的范围内,使用打开的< /代码>,你可能会遇到一个问题。需要做的是在退出带有open的块之前强制执行所有延迟结果
(defn process-csv [file]
(with-open [rdr (io/reader file)]
(doall (csv/parse-csv rdr))))
函数doall
用于确保实现整个延迟序列
由于您输入的内容的大小,另一种可能是安排自己关闭阅读器,然后将惰性用于其预期目的(仅在需要时生成结果)
我知道这已经得到了回答,但是这里有一个类似于@noisesmith的解决方案,它创建一个显式的延迟序列,如果到达输入的末尾,它会自动关闭
如果要延迟处理整个文件,这意味着您不必自己显式地管理句柄,否则会出现未解决的句柄问题
(defn lazy-read-csv
[csv-file]
(let [in-file (io/reader csv-file)
csv-seq (csv/read-csv in-file)
lazy (fn lazy [wrapped]
(lazy-seq
(if-let [s (seq wrapped)]
(cons (first s) (lazy (rest s)))
(.close in-file))))]
(lazy csv-seq)))
这是Eric Rochester的优秀作品中的内容。问题是,处理csv
函数并没有真正“处理”具有open
作用域的中的csv数据,而是将其作为延迟序列返回。当执行以open
范围退出时,流已经关闭。稍后尝试遍历惰性列表将引发异常
除非您确信CSV文件可以被读取并解析到内存中,否则我建议您不要遵循其他答案中的建议,即使用doall
强制评估具有open
作用域的中的惰性序列
相反,如果您希望将资源分配和取消分配部分与“更可重用”的业务逻辑分开,则应该执行以下操作:
(定义过程csv[rdr conn]
(doseq[行(csv/解析csv rdr):其中(通缉?行)]
(保存到自定义数据库表conn行)))
(defn启动[过程fn]
(让[csv文件(.getFile(clojure.java.io/resource“verbs.csv”))
(打开[rdr(jio/reader csv文件)
conn(数据库连接“测试”)]
(过程fn rdr conn)
(启动过程csv)
如您所见,process csv
函数以“抽象”的方式处理读卡器和数据库资源,也就是说,这些资源是可关闭的,使用后应该关闭。相反,资源的最终确定/关闭在start
函数中作为单独的事项处理
我还建议您研究Clojure协议,看看它们在上述类似场景中如何对资源进行抽象。如下所示,使用open不是懒惰的,而是解析csv。parse csv将延迟返回输入csv中的每个值,作为一个延迟的向量列表(每行一个向量)。我建议不要这样做,因为它可能导致资源无法回收。如果使用者没有遍历整个列表(例如,使用(take n coll)
,仅使用其中的一部分),则阅读器将永远无法正确关闭。正如我在解决方案中所说:“如果您将惰性地处理整个文件……否则您将面临未解决的处理问题”。这个解决方案明确地适用于您知道您将惰性地处理整个列表,并让它在自己之后自动清理。
(defn lazy-read-csv
[csv-file]
(let [in-file (io/reader csv-file)
csv-seq (csv/read-csv in-file)
lazy (fn lazy [wrapped]
(lazy-seq
(if-let [s (seq wrapped)]
(cons (first s) (lazy (rest s)))
(.close in-file))))]
(lazy csv-seq)))