我正在尝试使用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)
由于这是我第一次做这样的事情(异步+文件),也许我对它应该如何工作有一些误解。有人可以澄清将文件行发送到通道管道的正确方法是什么?
谢谢!
答案 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))
请参阅:
答案 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))