2011-10-13 31 views
153

我想知道在clojure 1.3中讀取和寫入文件的「推薦」方式。在Clojure 1.3中,如何讀取和寫入文件

  1. 如何讀取整個文件
  2. 如何通過線
  3. 讀取文件中的行如何寫一個新的文件
  4. 如何將行添加到現有的文件
+1

從谷歌第一個結果:HTTP: //lethain.com/reading-file-in-clojure/ – jcubic

+7

這個結果是從2009年起,有些東西最近已經改變了。 – Sergey

+9

確實。這個StackOverflow問題現在是Google的第一個結果。 – mydoghasworms

回答

255

假設我們只是做文字這裏,而不是文件的一些瘋狂的二進制的東西。

編號1:如何將整個文件讀入內存。

(slurp "/tmp/test.txt") 

當它是一個非常大的文件時不推薦。

2號:如何逐行讀取文件。

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

with-open的宏負責讀者在所述主體的端部封閉。閱讀器功能強制一個字符串(它也可以做一個URL等)到BufferedReaderline-seq提供了一個懶惰的seq。要求懶惰seq結果的下一個元素成爲從閱讀器讀取的行。

請注意,從Clojure 1.7開始,您還可以使用transducers來閱讀文本文件。

編號3:如何寫入新文件。

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

再次,with-open照顧該BufferedWriter在所述主體的端部封閉。作家強制將字符串轉換成BufferedWriter,您使用使用通過Java的互操作:(.write wrtr "something").

您還可以使用spitslurp相反:

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

4:即行追加到現有文件。

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

與上面相同,但現在帶有附加選項。

spit

再或者,的slurp相反:

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

PS:更明確的事實,你是讀取和寫入文件,而不是別的東西,你可以先創建一個文件對象,然後強迫它變成一個BufferedReader或作家:

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

文件功能也在clojure.java.io。

PS2:有時可以很方便地看到當前目錄(如「。」)是什麼。你可以在兩種方式的絕對路徑:

(System/getProperty "user.dir") 

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

非常感謝您的詳細解答。 我很高興在1.3中瞭解文件IO(文本文件)的推薦方式。 似乎已經有關文件的一些庫IO (clojure.contrb.io,clojure.contrib.duck流 並直接使用Java的BufferedReader的InputStreamReader的FileInputStream一些例子) 這讓我更加混亂。 此外幾乎沒有關於Clojure 1.3的信息 特別是日語(我的自然語言) 謝謝。 –

+0

嗨快樂聖,TNX接受我的答案!爲了您的信息,clojure.contrib.duck-streams現在已被棄用。這可能會增加混亂。 –

+0

非常豐富。謝謝。 – octopusgrabbus

29

如果文件適合內存,您可以使用slurp和spit進行讀寫:

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

(現S包含一個文件的內容作爲一個字符串)

(spit "newfile.txt" s) 

這將創建newfile.txt如果它不退出,並寫入文件內容。 如果要附加到文件中,你可以做

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

要讀取或寫入文件面向行,你會使用Java的讀者和作家。它們被包裹在命名空間clojure.java.io:

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

需要注意的是啜食/吐和讀/寫器實例之間的區別在於,文件保持打開狀態(在咱們語句)在後者和讀取和寫入被緩衝,因此在反覆讀取/寫入文件時效率更高。

下面是詳細信息:slurpspit clojure.java.io Java's BufferedReader Java's Writer

+1

謝謝保羅。我可以通過您的代碼和您的意見了解更多,這些意見在回答我的問題時側重點很明確。非常感謝你。 –

+0

感謝您在Michiel Borkent(典型案例)最佳方法答案中提供的稍低級別方法的信息。 – Mars

+0

@Mars謝謝。其實我先回答了這個問題,但Michiel的答案有更多的結構,而且似乎很受歡迎。 – Paul

6

關於第二個問題,人們有時要行的流返回第一類對象。爲了得到這個作爲一個懶惰的序列,並仍然在文件EOF自動關閉,我使用這個功能:

(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

我不認爲嵌套'defn'是意識形態Clojure。據我所知,你的'read-next-line'在你的'read-lines'函數之外是可見的。你可能使用了'(let [read-next-line(fn [] ...))''來代替。 – kristianlm

1

這是如何讀取整個文件。

如果該文件是在資源目錄,你可以這樣做:

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

記住需要/使用clojure.java.io

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