2011-10-13 6 views
153

Chciałbym poznać "zalecany" sposób czytania i pisania pliku w clojure 1.3.W Clojure 1.3, Jak czytać i pisać plik

  1. Jak odczytać cały plik
  2. Jak odczytać pliku linia po linii
  3. Jak napisać nowy plik
  4. Jak dodać wiersz do istniejącego pliku
+1

Pierwszy wynik z google: http: //lethain.com/reading-file-in-clojure/ – jcubic

+7

Ten wynik pochodzi z 2009 roku, niektóre rzeczy zostały ostatnio zmienione. – Sergey

+9

Rzeczywiście. To pytanie StackOverflow jest teraz pierwszym wynikiem w Google. – mydoghasworms

Odpowiedz

255

Zakładając jesteśmy tylko robi plików tekstowych tutaj i nie jakieś szalone rzeczy binarny.

Numer 1: jak odczytać cały plik do pamięci.

(slurp "/tmp/test.txt") 

Niezalecane, gdy jest to naprawdę duży plik.

Numer 2: jak czytać wiersz pliku po linii.

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

with-open makro dba, że ​​czytelnik jest zamknięty na końcu korpusu. Funkcja czytnika wymusza łańcuch (może to również zrobić adres URL itp.) Na BufferedReader. line-seq zapewnia leniwy seq. Żądanie następnego elementu leniwego seqa prowadzi do przeczytania linii od czytelnika.

Należy pamiętać, że od wersji Clojure 1.7 można również używać transducers do odczytywania plików tekstowych.

Numer 3: jak pisać do nowego pliku.

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

Znowu with-open dba że BufferedWriter jest zamknięty na końcu korpusu. Writer wymusza ciąg w BufferedWriter, że używasz wykorzystania poprzez java współdziałanie: (.write wrtr "something").

Można również użyć spit, przeciwieństwem slurp:

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

Numer 4: dołącz linię do istniejącego pliku .

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

To samo co powyżej, ale teraz z opcją dołączania.

Albo dzięki spit, przeciwieństwem slurp:

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

PS: Aby być bardziej wyraźnie o tym, że jesteś czytanie i pisanie do pliku, a nie coś innego, można najpierw utworzyć obiekt File, a następnie zmusić go do BufferedReader lub Writer:

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

funkcja plik jest również w clojure.java.io.

PS2: Czasami jest to przydatne, aby móc zobaczyć, co jest aktualnym katalogiem (czyli "."). Można uzyskać bezwzględną ścieżkę na dwa sposoby:

(System/getProperty "user.dir") 

lub

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

Dziękuję bardzo za szczegółową odpowiedź. Cieszę się, że mogę poznać zalecany sposób pliku IO (plik tekstowy) w wersji 1.3. Wydaje się być niektóre biblioteki o pliku IO (clojure.contrb.io, clojure.contrib.duck strumieni a niektóre przykłady bezpośrednio przy użyciu języka Java BufferedReader FileInputStream InputStreamReader) która mnie bardziej mylące. Ponadto niewiele jest informacji o Clojure 1.3 , szczególnie w języku japońskim (mój język naturalny) Dziękuję. –

+0

Cześć jolly-san, tnx za zaakceptowanie mojej odpowiedzi! Dla twojej informacji, clojure.contrib.duck-streams jest teraz przestarzałe. To prawdopodobnie przyczynia się do zamieszania. –

+0

Bardzo pouczające. Dzięki. – octopusgrabbus

29

Jeśli plik mieści się w pamięci, możesz go odczytać i zapisać za pomocą slurp and spit:

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

(y teraz zawiera zawartość pliku jako ciąg)

(spit "newfile.txt" s) 

Stwarza newfile.txt jeśli nie robi zjazd i zapisuje zawartość pliku. Jeśli chcesz dołączyć do pliku można zrobić

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

do odczytu lub zapisu pliku linewise byłoby użyć czytnika i pisarz Javy. Są one zapakowane w clojure.java.io przestrzeni nazw:

(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!" 

że różnica między SLURP/rożnie a czytelnik przykłady/Writer to, że plik jest otwarty (w sprawozdaniu let) w drugim i odczyt i zapis są buforowane, dzięki czemu są bardziej wydajne przy wielokrotnym odczycie/zapisie do pliku.

Oto więcej informacji: slurpspit clojure.java.io Java's BufferedReader Java's Writer

+1

Dziękuję Paul. Mogę nauczyć się więcej dzięki twoim kodeksom i twoim komentarzom, które są jasne w punkcie skupiającym się na odpowiedzi na moje pytanie. Dziękuję Ci bardzo. –

+0

Dzięki za dodanie informacji o metodach nieco niższego poziomu, nie podanych w znakomitej odpowiedzi Michiel Borkent na temat najlepszych metod dla typowych przypadków. – Mars

+0

@Mars Dzięki. Właściwie to odpowiedziałem na to pytanie w pierwszej kolejności, ale odpowiedź Michiel ma większą strukturę i wydaje się być bardzo popularna. – Paul

6

Odnośnie pytania 2, jeden czasami chce strumień linii zwracany jako obiekt pierwszej klasy. Aby uzyskać to jako leniwe sekwencji i jeszcze plik automatycznie zamknięte na EOF, użyłem tej funkcji:

(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

Nie sądzę, że gniazdowanie 'defn' jest ideologiczne Clojure. Twój "read-next-line", o ile rozumiem, jest widoczny poza twoją funkcją "read-lines". Możliwe, że zamiast tego użyłeś '(let [read-next-line (fn [] ...)). – kristianlm

1

ten sposób, aby przeczytać cały plik.

Jeśli plik znajduje się w katalogu zasobów, można to zrobić:

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

pamiętać, aby wymagać/używanie clojure.java.io

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