使用Clojure的core.async消耗文件内容

时间:2016-07-13 00:30:58

标签: clojure core.async

我正在尝试使用Clojure的core.async库来处理/处理文件中的行。当我的代码执行时,抛出IOException: Stream closed。下面是一个REPL会话,它重现了与我的代码相同的问题:

(require '[clojure.core.async :as async])
(require '[clojure.java.io :as io])

; my real code is a bit more involved with calls to drop, map, filter
; following line-seq
(def lines
  (with-open [reader (io/reader "my-file.txt")]
    (line-seq reader)))

(def ch
  (let [c (async/chan)]
    (async/go
      (doseq [ln lines]
        (async/>! c ln))
      (async/close! c))
    c))

; line that causes the error
; java.io.IOException: Stream closed
(async/<!! ch)

由于这是我第一次做这样的事情(异步+文件),也许我对它应该如何工作有一些误解。有人可以澄清将文件行发送到通道管道的正确方法是什么?

谢谢!

2 个答案:

答案 0 :(得分:4)

您的问题是with-open声明。退出此范围后,文件将立即关闭。因此,您打开line-seq然后在读取任何行之前关闭文件。

对于使用slurp功能的大多数文件,您会更好:

(require '[clojure.string :as str])

(def file-as-str   (slurp "my-file.txt"))
(def lines         (str/split-lines file-as-str))

请参阅:

http://clojuredocs.org/clojure.core/slurp

http://clojuredocs.org/clojure.string/split-lines

答案 1 :(得分:4)

作为@Alan pointed out,您对lines的定义会在不读取所有行的情况下关闭文件,因为line-seq会返回一个惰性序列。如果您扩展使用with-open宏...

(macroexpand-1
 '(with-open [reader (io/reader "my-file.txt")]
    (line-seq reader)))

...你明白了:

(clojure.core/let [reader (io/reader "my-file.txt")]
  (try
    (clojure.core/with-open []
      (line-seq reader))
    (finally
      (. reader clojure.core/close))))

您可以通过在完成阅读后关闭文件来解决此问题,而不是立即解决:

(def ch
  (let [c (async/chan)]
    (async/go
      (with-open [reader (io/reader "my-file.txt")]
        (doseq [ln (line-seq reader)]
          (async/>! c ln)))
      (async/close! c))
    c))