我想知道在clojure 1.3中读写文件的“推荐”方式。
- 如何读取整个文件
- 如何逐行读取文件
- 如何写一个新文件
- 如何在现有文件中添加行
我想知道在clojure 1.3中读写文件的“推荐”方式。
Answers:
假设我们只在这里做文本文件,而不是一些疯狂的二进制文件。
数字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
宏照顾读者在身体的一端封闭。reader函数将字符串(也可以执行URL等)强制转换为BufferedReader
。line-seq
传送一个懒惰序列。要求惰性seq的下一个元素导致从读取器读取一行。
请注意,从Clojure 1.7起,您还可以使用换能器读取文本文件。
第三:如何写入新文件。
(use 'clojure.java.io)
(with-open [wrtr (writer "/tmp/test.txt")]
(.write wrtr "Line to be written"))
同样,请with-open
注意BufferedWriter
在主体末端将其关闭。Writer将字符串强制转换为BufferedWriter
,您可以通过java interop使用该字符串:(.write wrtr "something").
您也可以使用spit
,相反于slurp
:
(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:要更明确地了解您正在读写文件而不是其他东西,您可以先创建一个File对象,然后将其强制为a 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的答案取得了很好的结果。
(with-open [rdr (reader "/tmp/test.txt")] (doall (line-seq rdr)))
doseq
返回nil
可能会导致悲伤的无返回值时间。
如果文件适合内存,则可以使用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!"
请注意,slurp / spit与读取器/写入器示例之间的区别是文件在后者中保持打开状态(在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 [] ...))
。
(require '[clojure.java.io :as io])
(io/copy (io/file "/etc/passwd") \*out*\)