在 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 等)强制转换为 BufferedReaderline-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 中,如何读写文件的主要内容,如果未能解决你的问题,请参考以下文章

C 语言文件操作 ( 按照文本行的方式读写文件 | fgets 函数 | fputs 函数 )

c语言如何读写二进制

使用phpexcel类读写excel文件

python读写csv时中文乱码问题解决办法

Python代码中打开txt文件(非读写)

请问VB如何读写二进制文件