在 Clojure 1.3中,如何读写文件

我想知道在 clojure 1.3中读写文件的“推荐”方法。

  1. 如何读取整个文件
  2. 如何逐行读取文件
  3. 如何写一个新的文件
  4. 如何向现有文件添加一行
76333 次浏览

如果这个文件适合内存,你可以读写它:

(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/spay 和 reader/writer 示例之间的区别在于,后者中的文件保持打开状态(在 let 语句中) ,读写缓冲,因此在重复读/写文件时效率更高。

这里有更多的信息: 吃东西 < a href = “ http://clojuredocs.org/clojure _ core/clojure.core/spit”rel = “ norefrer”> spat Clojure.java.io Java 的 BufferedReader Java 的作者

假设我们只是在处理文本文件而不是什么二进制文件。

第一: 如何将整个文件读入内存。

(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 函数将一个字符串(它还可以执行 URL 等操作)强制转换为 BufferedReaderline-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 互操作使用它: (.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"))

与上面相同,但现在使用追加选项。

或者,与 slurp相反的 spit:

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

PS: 为了更明确地说明你正在读写一个 File 而不是别的什么,你可以首先创建一个 File 对象,然后强制它成为一个 BufferedReader或 Writer:

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

File 函数也在 clojure.java.io 中。

PS2: 有时候能够看到工作目录是很方便的是。有两种方法可以得到绝对路径:

(System/getProperty "user.dir")

或者

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

关于问题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)))

这是如何读取整个文件。

如果文件在资源目录中,可以这样做:

(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*\)

要逐行读取文件,您不再需要使用 interop:

(->> "data.csv"
io/resource
io/reader
line-seq
(drop 1))

这假设您的数据文件保存在 resources 目录中,并且第一行是可以丢弃的头信息。