2016-07-13 18 views
1

Ich versuche Clojures core.async-Bibliothek zu verwenden, um Zeilen aus einer Datei zu verarbeiten/zu verarbeiten. Wenn mein Code ausgeführt wird, wird IOException: Stream closed geworfen. Nachfolgend finden Sie eine REPL Sitzung, die das gleiche Problem wie in meinem Code reproduziert:Dateiinhalte mit Clojures core.async konsumieren

(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) 

Seit seiner ist mein erstes Mal tut so etwas wie dieses (async + Datei) funktionieren sollte, vielleicht habe ich einige falsche Vorstellungen darüber, wie es. Kann jemand klären, welcher Ansatz für das Senden von Dateizeilen in eine Kanalleitung geeignet ist?

Danke!

Antwort

4

Ihr Problem ist die with-open Aussage. Die Datei wird geschlossen, sobald dieser Bereich verlassen wird. Also, öffnen Sie eine line-seq und dann schließen Sie die Datei vor dem Lesen von Zeilen.

werden Sie besser dran, für die meisten Dateien mit der slurp Funktion:

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

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

See:

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

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

+2

Ich sehe ... Ich wollte vermeiden, den gesamten Inhalt der Datei auf einmal in Speicher zu laden, weil es ein großer sein kann. Deshalb habe ich über die Verwendung von with-open/line-seq combo nachgedacht. Kann ich angesichts dieser Einschränkung (Dateigröße) etwas tun? –

4

Wie @Alan pointed out, Ihre Definition von lines schließt die Datei ohne alle seine Zeilen zu lesen, weil line-seq gibt eine Lazy-Sequenz zurück. Wenn Sie Ihre Nutzung der Makro with-open erweitern ...

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

... Sie diese:

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

Sie dieses Problem durch Schließen der Datei beheben, nachdem Sie von ihm gelesen haben, eher als sofort:

(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)) 
+0

Also war es wirklich ein Missverständnis von mir ... Ich dachte, dass "mit-offen" den geöffneten Leser irgendwie "behalten" würde, bis er erschöpft war. Ich werde Alans Antwort akzeptieren, da er zuerst auf das Problem und dann auf Ihr Problem hingewiesen hat. :-) Aber vielen Dank für die weitere Klärung der Dinge. –