2011-10-13 12 views
153

Mi piacerebbe conoscere il modo "consigliato" di leggere e scrivere un file in clojure 1.3.In Clojure 1.3, Come leggere e scrivere un file

  1. Come leggere l'intero file
  2. Come leggere un file riga per riga
  3. Come scrivere un nuovo file
  4. Come aggiungere una riga a un file esistente
+1

primo risultato di Google: http: //lethain.com/reading-file-in-clojure/ – jcubic

+7

Questo risultato è del 2009, alcune cose sono state cambiate ultimamente. – Sergey

+9

Infatti. Questa domanda StackOverflow è ora il primo risultato su Google. – mydoghasworms

risposta

255

Supponendo che stiamo solo facendo file di testo qui e non certo roba binario pazzo.

Numero 1: come leggere un intero file in memoria.

(slurp "/tmp/test.txt") 

Non consigliato quando si tratta di un file molto grande.

Numero 2: come leggere un file riga per riga.

(use 'clojure.java.io) 
(with-open [rdr (reader "/tmp/test.txt")] 
    (doseq [line (line-seq rdr)] 
    (println line))) 

Il with-open macro prende cura che il lettore è chiuso alla estremità del corpo. La funzione lettore costringe una stringa (può anche fare un URL, ecc.) In uno BufferedReader. line-seq consegna un seq pigro. Richiedere il prossimo elemento dei risultati lazy seq in una riga viene letta dal lettore.

Si noti che da Clojure 1.7 in poi, è anche possibile utilizzare transducers per leggere i file di testo.

Numero 3: come scrivere in un nuovo file.

(use 'clojure.java.io) 
(with-open [wrtr (writer "/tmp/test.txt")] 
    (.write wrtr "Line to be written")) 

Nuovamente, with-open prende cura che il BufferedWriter è chiusa alla fine del corpo. Writer costringe una stringa in un BufferedWriter, che si utilizza l'utilizzo tramite Java interoperabilità: (.write wrtr "something").

Si potrebbe anche usare spit, l'opposto di slurp:

(spit "/tmp/test.txt" "Line to be written") 

Numero 4: aggiungere una riga a un file esistente .

(use 'clojure.java.io) 
(with-open [wrtr (writer "/tmp/test.txt" :append true)] 
    (.write wrtr "Line to be appended")) 

Come sopra, ma ora con opzione di aggiunta.

O ancora con spit, l'opposto di slurp:

(spit "/tmp/test.txt" "Line to be written" :append true) 

PS: essere più espliciti circa il fatto che si sta leggendo e scrivendo in un file e non qualcosa d'altro, si poteva creare prima un oggetto File e poi costringere in un Writer BufferedReader o:

(reader (file "/tmp/test.txt")) 
;; or 
(writer (file "tmp/test.txt")) 

funzione Il file è anche in clojure.java.io.

PS2: A volte è utile essere in grado di vedere quale sia la directory corrente (così "."). È possibile ottenere il percorso assoluto in due modi:

(System/getProperty "user.dir") 

o

(-> (java.io.File. ".") .getAbsolutePath) 
+1

Grazie mille per la tua risposta dettagliata. Sono contento di conoscere il modo consigliato di File IO (file di testo) in 1.3. Ci sembra essere stato alcune librerie su File IO (clojure.contrb.io, clojure.contrib.duck-streams e alcuni esempi direttamente utilizzando Java BufferedReader FileInputStream InputStreamReader) che mi ha reso più confusa. Inoltre ci sono poche informazioni su Clojure 1.3 soprattutto in giapponese (la mia lingua naturale) Grazie. –

+0

Ciao jolly-san, per aver accettato la mia risposta! Per vostra informazione, clojure.contrib.duck-stream è ora deprecato. Questo forse aggiunge alla confusione. –

+0

Molto informativo. Grazie. – octopusgrabbus

29

Se il file si adatta alla memoria, puoi leggerlo e scriverlo con slurp e spit:

(def s (slurp "filename.txt")) 

(S contiene ora il contenuto di un file come una stringa)

(spit "newfile.txt" s) 

Questo crea newfile.txt se pretende di uscita e scrive il contenuto del file. Se si desidera aggiungere al file che si può fare

(spit "filename.txt" s :append true) 

Per leggere o scrivere un file formato da linee si usa lettore e scrittore di Java. Sono avvolti nel clojure.java.io namespace:

(ns file.test 
    (:require [clojure.java.io :as io])) 

(let [wrtr (io/writer "test.txt")] 
    (.write wrtr "hello, world!\n") 
    (.close wrtr)) 

(let [wrtr (io/writer "test.txt" :append true)] 
    (.write wrtr "hello again!") 
    (.close wrtr)) 

(let [rdr (io/reader "test.txt")] 
    (println (.readLine rdr)) 
    (println (.readLine rdr))) 
; "hello, world!" 
; "hello again!" 

nota che la differenza tra slurp/spiedo e il lettore/scrittore esempi è che il file rimane aperto (nei prospetti LET) in queste e la la lettura e la scrittura sono bufferizzate, quindi più efficienti quando si legge/scrive ripetutamente su un file.

Ecco ulteriori informazioni: slurpspit clojure.java.io Java's BufferedReader Java's Writer

+1

Grazie, Paul. Potrei imparare di più dai tuoi codici e dai tuoi commenti che sono chiari nel punto che si concentra sulla risposta alla mia domanda. Grazie mille. –

+0

Grazie per l'aggiunta di informazioni su metodi di livello leggermente inferiore non forniti nella risposta (eccellente) di Michiel Borkent sui metodi migliori per i casi tipici. – Mars

+0

@Mars Grazie. In realtà ho risposto prima a questa domanda, ma la risposta di Michiel ha più struttura e sembra essere molto popolare. – Paul

6

quanto riguarda la domanda 2, uno a volte vuole il flusso di linee restituito come un oggetto di prima classe. Per ottenere questo come una sequenza pigro, e hanno ancora il file chiuso automaticamente EOF, ho usato questa funzione:

(use 'clojure.java.io) 

(defn read-lines [filename] 
    (let [rdr (reader filename)] 
    (defn read-next-line [] 
     (if-let [line (.readLine rdr)] 
     (cons line (lazy-seq (read-next-line))) 
     (.close rdr))) 
    (lazy-seq (read-next-line))) 
) 

(defn echo-file [] 
    (doseq [line (read-lines "myfile.txt")] 
    (println line))) 
+5

Non penso che il nesting 'defn' sia un Clojure ideomatico. Il tuo 'read-next-line', per quanto ho capito, è visibile al di fuori della funzione' read-lines'. Potresti aver usato un '(lascia [read-next-line (fn [] ...))'. – kristianlm

1

Questo è come leggere l'intero file.

Se il file si trova nella directory di risorse, si può fare questo:

(let [file-content-str (slurp (clojure.java.io/resource "public/myfile.txt")])

ricordarsi di richiedere/uso clojure.java.io

0
(require '[clojure.java.io :as io]) 
(io/copy (io/file "/etc/passwd") \*out*\) 
Problemi correlati