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

时间:2018-12-16 15:31:41

标签: clojure io seq

template:"<a href='/XXX/XX?P0=#=ID#&p1="+ $("#MyDropdown").val() + "' target='_blank'>${xField} </a>"

我正在尝试逐行读取json文件的目录,以便我可以对数据进行延迟处理。

我不断收到 BufferedReader br1 = new BufferedReader(new InputStreamReader(new BufferedInputStream(new FileInputStream("name.txt")),"UTF-8")); BufferedReader br2 = new BufferedReader(new InputStreamReader(new FileInputStream("name.txt"),"UTF-8")); -如何在不过早关闭阅读器的情况下使用它?

2 个答案:

答案 0 :(得分:2)

with-open函数旨在阻止您执行此操作,因为文件句柄和其他操作系统资源是您应谨慎处理而不是懒惰的事情。您打算在with-open的动态范围内对文件内容进行所有处理。因此,您应该接受一个函数作为参数,而不是返回惰性序列,并在惰性序列上调用该函数,同时仍在with-open的范围内。该函数当然不应返回另一个延迟序列,而应在返回之前处理其整个输入。

所以这种事情的典型用法是这样的:

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

当您有with-open个序列列表时,情况会有些复杂-您不能只调用一次process。您可以做的一件事是返回在每个文件上调用process的结果的列表:

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

然后,如果您需要对此执行某些全局操作,则可以reduce覆盖process-files的结果。

答案 1 :(得分:1)

问题在于,当程序退出其所在的作用域时,with-open会调用.close,但此时并不一定已读取所有行。

我的解决方案可能是一个本来就不应该滥用的可恶行为,但是这里的想法是:创建一个仅调用lazy-seq的“ .close”,并将其连接到末尾line-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)))

通过对台式机上文件的快速测试,它似乎可以正常工作。如果将println添加到终结点lazy-seq中,它将按预期打印,因此文件 已关闭。

我很犹豫地建议这种解决方案,因为它依赖于在懒惰列表内部进行副作用,出于明显的原因,我已经习惯于“感到错误”。此方法的主要缺点是,除非对整个序列进行了评估,否则不会关闭文件,并且文件将一直保持打开状态,直到到达末尾为止。尽管有这些限制,但我看不出如何避免这些问题。


我意识到我在使用lazy-cat有点错误。我有一个多余的lazy-seq包装器。现在已修复。您也可以使用

之类的东西
(apply concat (line-seq rdr)
              (lazy-seq (do (.close rdr)
                            nil))))))

代替lazy-cat