In Clojure 1.3, How to read and write a file
我想知道在Clojure 1.3中读取和写入文件的"推荐"方式。
假设我们只是在这里做文本文件,而不是一些疯狂的二进制文件。
数字1:如何将整个文件读取到内存中。
1 | (slurp"/tmp/test.txt") |
当文件很大时不推荐使用。
第二:如何逐行读取文件。
1 2 3 4 | (use 'clojure.java.io) (with-open [rdr (reader"/tmp/test.txt")] (doseq [line (line-seq rdr)] (println line))) |
请注意,从Clojure 1.7开始,您还可以使用传感器读取文本文件。
数字3:如何写入新文件。
1 2 3 | (use 'clojure.java.io) (with-open [wrtr (writer"/tmp/test.txt")] (.write wrtr"Line to be written")) |
同样,
您也可以使用
1 | (spit"/tmp/test.txt""Line to be written") |
4:在现有文件中附加一行。
1 2 3 | (use 'clojure.java.io) (with-open [wrtr (writer"/tmp/test.txt" :append true)] (.write wrtr"Line to be appended")) |
和上面一样,但现在有了附加选项。
或者,与
1 | (spit"/tmp/test.txt""Line to be written" :append true) |
PS:为了更明确地说明您正在读取和写入文件而不是其他内容,您可以先创建一个文件对象,然后将其强制为
1 2 3 | (reader (file"/tmp/test.txt")) ;; or (writer (file"tmp/test.txt")) |
文件函数也在clojure.java.io中。
PS2:有时可以很方便地看到当前目录(所以".")是什么。您可以通过两种方式获得绝对路径:
1 | (System/getProperty"user.dir") |
或
1 | (-> (java.io.File.".") .getAbsolutePath) |
如果文件适合内存,您可以用slurp和spit读写它:
1 | (def s (slurp"filename.txt")) |
(S现在以字符串形式包含文件的内容)
1 | (spit"newfile.txt" s) |
如果不退出并写入文件内容,将创建newfile.txt。如果要附加到文件,可以执行以下操作
1 | (spit"filename.txt" s :append true) |
要以线性方式读取或写入文件,您将使用Java的读取器和写入器。它们被包装在名称空间clojure.java.io中:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | (ns file.test (:require [clojure.java.io :as io])) (let [wrtr (io/writer"test.txt")] (.write wrtr"hello, world! ") (.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语句中),并且读写被缓冲,因此在重复读/写文件时效率更高。
这里有更多的信息:口水白头翁Java缓冲读卡器爪哇作家
关于问题2,有时需要将行流作为第一类对象返回。为了将其作为一个惰性序列,并在eof时仍然自动关闭文件,我使用了以下函数:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | (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))) |
这是读取整个文件的方法。
如果文件在资源目录中,则可以执行以下操作:
记住需要/使用clojure.java.io
要逐行读取文件,您不再需要使用interop:
1 2 3 4 5 | (->>"data.csv" io/resource io/reader line-seq (drop 1)) |
这假设数据文件保存在资源目录中,第一行是可以丢弃的头信息。
1 2 | (require '[clojure.java.io :as io]) (io/copy (io/file"/etc/passwd") \*out*\) |