在Clojure 1.3中,如何读写文件

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)))

    with-open宏注意读卡器在正文末尾关闭。reader函数将字符串(它也可以执行URL等)强制转换为BufferedReaderline-seq提供了一个懒惰的序列。请求lazy seq的下一个元素会导致从读卡器读取一行。

    请注意,从Clojure 1.7开始,您还可以使用传感器读取文本文件。

    数字3:如何写入新文件。

    1
    2
    3
    (use 'clojure.java.io)
    (with-open [wrtr (writer"/tmp/test.txt")]
      (.write wrtr"Line to be written"))

    同样,with-open注意到BufferedWriter在身体末端关闭。作者将一个字符串强制转换为EDCOX1(4),通过Java互操作:EDCOX1(6)使用。

    您也可以使用spit,与slurp相反:

    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"))

    和上面一样,但现在有了附加选项。

    或者,与spit相反,与slurp相反:

    1
    (spit"/tmp/test.txt""Line to be written" :append true)

    PS:为了更明确地说明您正在读取和写入文件而不是其他内容,您可以先创建一个文件对象,然后将其强制为BufferedReader或writer:

    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)))


    这是读取整个文件的方法。

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

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

    记住需要/使用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*\)