IT-Swarm.Net

在Clojure 1.3中,如何读写文件

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

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

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

数字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。要求延迟seq的下一个元素导致从读取器读取的行。.

请注意,从Clojure 1.7开始,您还可以使用 transducer 来读取文本文件。.

数字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对象,然后将其强制转换为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)
263
Michiel Borkent

快速,可靠且价格合理的云托管

注册并在30天内获得$50奖金!

如果文件适合内存,你可以用Slurp和spit读取和写入:

(def s (Slurp "filename.txt"))

(现在包含文件的内容为字符串)

(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语句中)并且读取和写入被缓冲,因此在重复读取/写入文件时更有效。.

以下是更多信息: Slurpspitclojure.Java.ioJava的BufferedReaderJava的Writer

32
Paul

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

这是如何读取整个文件。.

如果文件位于资源目​​录中,则可以执行以下操作:

(let [file-content-str (Slurp (clojure.Java.io/resource "public/myfile.txt")])

记得要求/使用clojure.Java.io

1
joshua
(require '[clojure.Java.io :as io])
(io/copy (io/file "/etc/passwd") \*out*\)
0
Dima Fomin

要逐行读取文件,您不再需要求助于互操作:

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

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

0
Chris Murphy