2011-10-13 36 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*\)