在 Clojure 1.3 中,如何读写文件
Posted
技术标签:
【中文标题】在 Clojure 1.3 中,如何读写文件【英文标题】:In Clojure 1.3, How to read and write a file 【发布时间】:2011-12-07 02:24:04 【问题描述】:我想知道在 clojure 1.3 中读写文件的“推荐”方式。
-
如何读取整个文件
如何逐行读取文件
如何编写新文件
如何在现有文件中添加一行
【问题讨论】:
google 的第一个结果:lethain.com/reading-file-in-clojure 这个结果是 2009 年的,最近发生了一些变化。 确实如此。这个 *** 问题现在是 Google 上的第一个结果。 【参考方案1】:这是读取整个文件的方法。
如果文件在资源目录中,你可以这样做:
(let [file-content-str (slurp (clojure.java.io/resource "public/myfile.txt")])
记得要求/使用clojure.java.io
。
【讨论】:
【参考方案2】:要逐行读取文件,您不再需要求助于互操作:
(->> "data.csv"
io/resource
io/reader
line-seq
(drop 1))
这假定您的数据文件保存在资源目录中,并且第一行是可以丢弃的标头信息。
【讨论】:
【参考方案3】:假设我们在这里只处理文本文件而不是一些疯狂的二进制文件。
第 1 点:如何将整个文件读入内存。
(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 等)强制转换为 BufferedReader
。 line-seq
提供惰性序列。要求惰性序列的下一个元素会导致从阅读器读取一行。
请注意,从 Clojure 1.7 开始,您还可以使用 transducers 来读取文本文件。
第 3 点:如何写入新文件。
(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 而不是其他内容,您可以先创建一个 File 对象,然后将其强制转换为 BufferedReader
或作者:
(reader (file "/tmp/test.txt"))
;; or
(writer (file "tmp/test.txt"))
文件函数也在clojure.java.io中。
PS2: 有时可以很方便地查看当前目录(例如“.”)是什么。可以通过两种方式获取绝对路径:
(System/getProperty "user.dir")
或
(-> (java.io.File. ".") .getAbsolutePath)
【讨论】:
非常感谢您的详细解答。很高兴了解1.3中File 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 的回答效果很好。
这与懒惰有关。当您在 with-open 之外使用 line-seq 的结果时,当您将其结果打印到 REPL 时会发生这种情况,那么阅读器已经关闭。一个解决方案是将 line-seq 包装在一个 doall 中,这会立即强制评估。 (with-open [rdr (reader "/tmp/test.txt")] (doall (line-seq rdr)))
对于像我这样的实际初学者,请注意doseq
返回nil
,这可能会导致悲伤无返回值时间。【参考方案4】:
(require '[clojure.java.io :as io])
(io/copy (io/file "/etc/passwd") \*out*\)
【讨论】:
【参考方案5】:关于问题 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 [] ...))
。
我认为如果它返回创建的函数(关闭打开的阅读器)而不是全局绑定,您的答案会更好一些。【参考方案6】:
如果文件适合内存,您可以使用 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 和 reader/writer 示例之间的区别在于后者中的文件保持打开状态(在 let 语句中)并且读取和写入被缓冲,因此在重复读取/写入时效率更高一个文件。
这里有更多信息:slurpspit clojure.java.io Java's BufferedReader Java's Writer
【讨论】:
谢谢保罗。我可以通过您的代码和您的 cmets 了解更多信息,这些代码和 cmets 在重点回答我的问题时很清楚。非常感谢。 感谢您添加 Michiel Borkent 关于典型案例最佳方法的(优秀)答案中未提供的稍微低级方法的信息。 @Mars 谢谢。其实我确实先回答了这个问题,但是 Michiel 的回答结构更丰富,而且似乎很受欢迎。 他在通常情况下做得很好,但您提供了其他信息。这就是为什么 SE 允许多个答案是件好事。以上是关于在 Clojure 1.3 中,如何读写文件的主要内容,如果未能解决你的问题,请参考以下文章