在Clojure 1.3中,如何读写文件


163

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

  1. 如何读取整个文件
  2. 如何逐行读取文件
  3. 如何写一个新文件
  4. 如何在现有文件中添加行


8
这个结果是从2009年开始,最近发生了一些变化。
谢尔盖(Sergey)

11
确实。现在,这个StackOverflow问题是Google上的第一个结果。
mydoghasworms

Answers:


273

假设我们只在这里做文本文件,而不是一些疯狂的二进制文件。

数字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等)强制转换为BufferedReaderline-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)

1
非常感谢您的详细回答。我很高兴认识1.3中推荐的文件IO(文本文件)方式。似乎有一些关于文件IO的库(clojure.contrb.io,clojure.contrib.duck-streams以及一些直接使用Java BufferedReader FileInputStream InputStreamReader的示例),这使我更加困惑。此外,关于Clojure 1.3的信息很少,尤其是日语(我的自然语言)。谢谢。
jolly-san

嗨,乔利·桑,tnx接受了我的回答!仅供参考,clojure.contrib.duck-streams现在已弃用。这可能会增加混乱。
米歇尔·博肯特

也就是说,(with-open [rdr (reader "/tmp/test.txt")] (line-seq rdr))收益IOException Stream closed而不是行的集合。该怎么办?不过,@ satyagraha的答案取得了很好的结果。
2013年

4
这与懒惰有关。当您在with-open之外使用line-seq的结果时(将结果打印到REPL时会发生这种情况),则阅读器已经关闭。一种解决方案是将line-seq包裹在doall内,这会立即进行评估。(with-open [rdr (reader "/tmp/test.txt")] (doall (line-seq rdr)))
米歇尔·博肯特

对于像我这样的实际初学者,请注意,doseq返回nil可能会导致悲伤的无返回值时间。
2015年

33

如果文件适合内存,则可以使用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


1
谢谢保罗。通过您的代码和您的评论,我可以从中得到更多的了解,重点是回答我的问题。非常感谢你。
jolly-san

感谢您添加有关Michiel Borkent(针对典型案例的最佳方法)的(出色)答案中未提供的稍微低级方法的信息。
火星

@火星谢谢。实际上,我确实确实首先回答了这个问题,但是Michiel的回答更具结构性,而且似乎很受欢迎。
保罗

他在通常情况下做得很好,但您提供了其他信息。这就是为什么SE允许多个答案很好。
火星

6

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

7
我认为嵌套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

要逐行读取文件,您不再需要使用互操作:

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

这假定您的数据文件保留在资源目录中,并且第一行是可以丢弃的标头信息。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.