使用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)))