如何在Clojure中逐行惰性地读取文件目录

如何在Clojure中逐行惰性地读取文件目录,clojure,io,seq,Clojure,Io,Seq,我试图一行一行地惰性地读取json文件目录,这样我就可以惰性地对数据执行操作 我不断得到java.io.IOException:streamclosed——我如何在不过早关闭阅读器的情况下使用它 问题在于,当程序退出它所包含的作用域时,open调用。close,但所有行不一定都被该点读取 我的解决方案可能是一个本不该出现在光天化日之下的恶习,但我的想法是:创建一个“lazy seq”只调用.close,并将其连接到行seq列表的末尾: (->> "/Users/micahsmith/

我试图一行一行地惰性地读取json文件目录,这样我就可以惰性地对数据执行操作


我不断得到
java.io.IOException:streamclosed
——我如何在不过早关闭阅读器的情况下使用它

问题在于,当程序退出它所包含的作用域时,open调用
。close
,但所有行不一定都被该点读取

我的解决方案可能是一个本不该出现在光天化日之下的恶习,但我的想法是:创建一个“
lazy seq
”只调用
.close
,并将其连接到
行seq
列表的末尾:

(->> "/Users/micahsmith/printio/gooten-import-ai/jupyter/data"
     File.
     file-seq
     (filter #(-> ^File % .getAbsolutePath (str-contains? ".json")))
     (mapcat (fn [^File file]
            (with-open [ rdr (io/reader file)]
              (line-seq rdr)))))
从我对桌面上的文件的快速测试来看,它似乎可以工作。如果将
println
添加到终止的
lazy seq
中,它将按预期打印,因此文件将被关闭

不过,我不太愿意提出这个解决方案,因为它依赖于在懒散列表中执行副作用,而我习惯于“感觉错误”,原因很明显。此方法的主要缺点是,除非对整个序列进行求值,否则不会关闭文件,并且文件将一直保持打开状态,直到到达结束。考虑到这些限制,我不认为这两个问题都可以避免


我意识到我用的是“懒猫”有点错误。我有一个额外的、不必要的
lazy seq
包装器。现在修好了。你也可以用类似的东西

(defn lazy-lines [^File file]
  (let [rdr (io/reader file)]
    (lazy-cat (line-seq rdr)
              (do (.close rdr)
                  nil)))) ; Explicit nil to indicate termination

(defn get-lines [^String path]
  (->> path
       (File.)
       (file-seq)
       (filter #(-> ^File % (.getAbsolutePath) (clojure.string/includes? ".json")))
       (mapcat lazy-lines)))

使用打开功能的是为了阻止您这样做,而不是
懒猫

,因为您应该小心处理文件句柄和其他操作系统资源,而不是懒惰地处理。您打算使用open在
的动态范围内对文件内容进行所有处理。因此,您不应该返回惰性序列,而应该接受函数作为参数,并在仍然在open的
范围内的情况下对惰性序列调用该函数。该函数当然不应该返回另一个惰性序列,而是在返回之前处理其整个输入

所以这种东西的典型用途是:

(apply concat (line-seq rdr)
              (lazy-seq (do (.close rdr)
                            nil))))))
当您有一个带有打开序列的
列表时,情况会稍微复杂一些-您不能只调用
进程一次。您可以做的一件事是在每个文件上返回调用
process
的结果列表:

(defn process-file [filename process]
  (with-open [f (io/reader filename)]
    (process (line-seq f))))

然后,如果您需要对其执行一些全局操作,您可以
减少
处理文件的结果

这里的答案应该很有用:只是让人问一下:那些“json”文件可以逐行读取吗?e、 它们更像一行一个json对象?
(defn process-files [filenames process]
  (for [filename filenames]
    (with-open [f (io/reader filename)]
      (process (line-seq f)))))