我想知道在Clojure 1.3中读写文件的“推荐”方式。
- 如何读取整个文件
- 如何逐行读取文件
- 如何写入一个新文件
- 如何向现有文件添加一行
我想知道在Clojure 1.3中读写文件的“推荐”方式。
假设我们只处理文本文件,而不是一些疯狂的二进制文件。
第一步:如何将整个文件读入内存。
(slurp "/tmp/test.txt")
当文件非常大时,不建议使用此方法。
第二种方法:逐行读取文件。
(use 'clojure.java.io)
(with-open [rdr (reader "/tmp/test.txt")]
(doseq [line (line-seq rdr)]
(println line)))
with-open
宏会确保在主体结束时关闭reader。reader函数将字符串(也可以是URL等)强制转换为BufferedReader
。line-seq
返回一个惰性seq,要求惰性seq的下一个元素将导致从reader中读取一行。
请注意,从Clojure 1.7开始,您还可以使用transducers读取文本文件。
第三点:如何写入新文件。(use 'clojure.java.io)
(with-open [wrtr (writer "/tmp/test.txt")]
(.write wrtr "Line to be written"))
使用with-open
时,它会在代码块结束时关闭BufferedWriter
。 Writer将字符串转换为BufferedWriter
,您可以通过Java互操作使用它:(.write wrtr "something")。
您还可以使用spit
,它是slurp
的相反操作:
(spit "/tmp/test.txt" "Line to be written")
第四步:向现有文件追加一行。
(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)
提示:为了更明确地表示你正在读写文件,而不是其他某些内容,你可以先创建一个文件对象,然后将其强制转换为BufferedReader
或Writer
:
(reader (file "/tmp/test.txt"))
;; or
(writer (file "tmp/test.txt"))
文件函数也在clojure.java.io中。
PS2:有时候能够查看当前目录(即“。”)是很方便的。你可以通过两种方式获取绝对路径:
(System/getProperty "user.dir")
或者(-> (java.io.File. ".") .getAbsolutePath)
(with-open [rdr (reader "/tmp/test.txt")] (line-seq rdr))
返回的是 IOException Stream closed
而不是一系列的行。该怎么办?不过,我已经从 @satyagraha 的答案中得到了很好的结果。 - 0dB(with-open [rdr (reader "/tmp/test.txt")] (doall (line-seq rdr)))
- Michiel Borkentdoseq
返回 nil
,这可能会导致没有返回值的悲伤时刻。 - Oct如果文件可以放入内存中,你可以使用 slurp 和 spit 来读写它:
(def s (slurp "filename.txt"))
(s现在包含一个文件的内容作为字符串)
(spit "newfile.txt" s)
这将创建newfile.txt文件,如果不存在则写入文件内容。如果你想追加到文件中,可以这样做。(spit "filename.txt" s :append true)
要逐行读写文件,您需要使用Java的reader和writer。它们被包装在命名空间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!"
请注意 slurp/spit 和 reader/writer 示例之间的区别在于后者中文件保持打开状态(在 let 语句中),而且读取和写入是缓存的,因此在反复从/向文件中读取/写入时更加高效。有关更多信息,请参见以下链接:slurp、spit、clojure.java.io、Java 的 BufferedReader、Java 的 Writer。关于问题2,有时候我们希望将行的流作为一等对象返回。为了得到这样的惰性序列,同时在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)))
defn
嵌套并不符合 Clojure 的惯用法。据我理解,你的 read-next-line
函数在 read-lines
函数外也可以被访问到。你或许可以使用 (let [read-next-line (fn [] ...))
代替。 - kristianlm现在,您不再需要使用interop来逐行读取文件:
(->> "data.csv"
io/resource
io/reader
line-seq
(drop 1))
(let [file-content-str (slurp (clojure.java.io/resource "public/myfile.txt")])
clojure.java.io
。(require '[clojure.java.io :as io])
(io/copy (io/file "/etc/passwd") \*out*\)