2013-10-28 13 views
5

Ich habe bisher zu analysieren:clojure-csv.core mit einer großen CSV-Datei

(:require [clojure-csv.core :as csv]) 
(:require [clojure.java.io :as io])) 

(def csv-file (.getFile (clojure.java.io/resource "verbs.csv"))) 

(defn process-csv [file] 
    (with-open [rdr (io/reader file)] 
    (csv/parse-csv rdr))) 

Aber ich bin immer java.io.IOException: Stream closed. Ich verwende clojure-csv und es stellt zwei Methoden, von denen die erste die ich benutze, parse-csv, sagt der Doc:

Takes a CSV as a char sequence or string, and returns a lazy sequence of vectors of strings 

Was ich glaube, ich weiß: with-open faul ist, und die rdr in (csv/parse-csv rdr))) ist eine einzige Zeile der CSV-Datei richtig?

PS. Ich würde auch gerne die Datei durchsuchen, ist es teuer, die Datei immer wieder zu öffnen (obwohl sie faul gelesen wird) und den gesamten Inhalt zu durchsuchen?

+0

Wie ich unten zeige, ist with-open nicht faul, aber parse-csv ist. parse-csv wird jeden Wert in der Eingabe csv träge zurückgeben, als eine faule Liste von Vektoren (ein Vektor pro Zeile). – noisesmith

Antwort

10

with-open ist nicht faul, aber wenn Sie etwas faul im Inneren with-open tun, können Sie auf ein Problem stoßen, wenn die faulen Aktion nicht in den Anwendungsbereich der with-open gezwungen wird. Was getan werden muss, ist es, alle faulen Ergebnisse zu erzwingen, bevor der Block with-open verlassen wird.

(defn process-csv [file] 
    (with-open [rdr (io/reader file)] 
    (doall (csv/parse-csv rdr)))) 

die Funktion doall ist, dass er die Gesamtheit eines faulen Sequenz realisiert ausgelegt ist.

Aufgrund der Größe Ihrer Eingabe wäre es eine andere Möglichkeit, das Lesegerät selbst zu schließen und dann die Faulheit für den beabsichtigten Zweck zu verwenden (die Ergebnisse werden nur dann generiert, wenn Sie sie benötigen).

(defn find-results 
[stream] 
(for [record stream 
     :while (seq (first record))] 
    record)) 

(def rdr (io/reader "verbs.csv")) 
(def csv (csv/parse-csv rdr)) 

(def results (doall (find-results csv))) 

(.close rdr) 
0

Sieht so aus, als ob Datei versucht wird, träge außerhalb des Formulars with-open zu analysieren, wenn die Datei bereits geschlossen ist.

Versuch, so etwas zu überprüfen, druckt die ersten 5 Zeilen analysiert:

(defn process-csv [file] 
    (with-open [rdr (io/reader file)] 
    (let [lines (csv/parse-csv rdr)] 
     (doseq [l (take 5 lines)] 
      (println l))))) 

Ich glaube nicht, die Datei oft öffnen, um die Suche etwas in teuer im Vergleich wäre, wenn die Datei sehr groß ist.

Wenn Sie es oft tun müssen, würde ich in Betracht ziehen, eine Art von Suchindex zu erstellen.

7

Ich weiß, das bereits beantwortet, aber hier ist eine ähnliche Lösung, indem eine explizite faul Sequenz @noisesmith, das Auto schließt, wenn Sie das Ende des Eingangs erreichen.

Wenn Sie die gesamte Datei langsam verarbeiten möchten, bedeutet dies, dass Sie die Handles nicht explizit selbst verwalten müssen, da sonst Probleme mit dem offenen Handle auftreten. Diese

(defn lazy-read-csv 
    [csv-file] 
    (let [in-file (io/reader csv-file) 
     csv-seq (csv/read-csv in-file) 
     lazy (fn lazy [wrapped] 
       (lazy-seq 
       (if-let [s (seq wrapped)] 
        (cons (first s) (lazy (rest s))) 
        (.close in-file))))] 
    (lazy csv-seq))) 

ist von der ausgezeichneten Clojure Data Analysis Cookbook von Eric Rochester

+0

Ich würde gegen etwas wie dieses empfehlen, da es potenziell dazu führen kann, dass Ressourcen nicht zurückgefordert werden. Wenn der Verbraucher nicht die gesamte Liste durchläuft (z. B. nur einen Teil davon verbraucht mit "(take n coll)"), wird der Leser niemals richtig geschlossen. –

+1

Wie ich in meiner Lösung sagte: "Wenn Sie die gesamte Datei träge verarbeiten werden ... sonst haben Sie offene Handle-Probleme". Diese Lösung ist explizit für, wenn Sie wissen, dass Sie die gesamte Liste träge verarbeiten und es automatisch nach sich selbst bereinigen werden. –

1

Das Problem ist, dass Ihre process-csv Funktion nicht wirklich „Prozess“ die CSV-Daten innerhalb der with-open Umfang, sondern sie als faul Sequenz zurück. Wenn die Ausführung den Bereich with-open verlässt, ist der Stream bereits geschlossen. Wenn Sie später versuchen, die Lazy-List zu durchlaufen, wird die Ausnahme ausgelöst.

Wenn Sie sicher sind, dass die CSV-Datei in den Speicher zusammen gelesen und analysiert werden kann, würde ich vorschlagen, die nicht zu folgen, was in anderen Antworten empfohlen wird, dh zu zwingen Auswertung der lazy-Sequenz innerhalb des with-open Umfangs doall mit .

Stattdessen, wenn Sie es vorziehen, die Ressourcenzuweisung und de-Zuweisung Teil aus dem „mehr wiederverwendbar“ Business-Logik zu trennen, sollten Sie etwas tun:

(defn process-csv [rdr conn] 
    (doseq [row (csv/parse-csv rdr) :where (wanted? row)] 
    (save-to-custom-database-table conn row))) 

(defn start [process-fn] 
    (let [csv-file (.getFile (clojure.java.io/resource "verbs.csv"))] 
    (with-open [rdr (jio/reader csv-file) 
       conn (database-connection "TEST")] 
     (process-fn rdr conn)))) 

(start process-csv) 

Wie Sie sehen können, die process-csv Funktion handhabt die Leser- und Datenbankressourcen in einer "abstrakten" Art und Weise, dh sie wird nicht damit belästigt, dass diese Ressourcen Closeable sind und nach der Verwendung geschlossen werden sollten. Stattdessen wird die Finalisierung/Schließung der Ressourcen in der Funktion als separate Angelegenheit behandelt.

Ich würde Ihnen auch empfehlen, in Clojure-Protokolle zu schauen und zu sehen, wie sie nützlich sind, Ressourcen in ähnlichen Szenarien wie dem oben genannten zu abstrahieren.