Clojure 1.3でファイルを読み書きする「推奨」方法を知りたいのですが。
ここではテキストファイルのみを実行し、クレイジーなバイナリファイルは実行しないものとします。
番号1:ファイル全体をメモリに読み込む方法
(Slurp "/tmp/test.txt")
本当に大きなファイルの場合はお勧めしません。
数2:ファイルを1行ずつ読み込む方法
(use 'clojure.Java.io)
(with-open [rdr (reader "/tmp/test.txt")]
(doseq [line (line-seq rdr)]
(println line)))
with-open
マクロは、リーダーが本文の最後で閉じられるようにします。リーダー関数は、文字列(URLなども可能)をBufferedReader
に強制します。 line-seq
は遅延シーケンスを提供します。レイジー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
が本文の最後で閉じられるように注意します。ライターは、Java interopを介して使用するBufferedWriter
に文字列を強制します:(.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:現在のディレクトリ( "。")が何であるかを確認できると便利な場合があります。次の2つの方法で絶対パスを取得できます。
(System/getProperty "user.dir")
または
(-> (Java.io.File. ".") .getAbsolutePath)
ファイルがメモリに収まる場合は、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ステートメントで)開いたままであり、読み取りと書き込みがバッファリングされるため、ファイルへの読み取り/書き込みを繰り返し行う場合により効率的であることに注意してください。
詳細は次のとおりです。 Slurpspitclojure.Java.ioJava's BufferedReaderJava's Writer =
質問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)))
これは、ファイル全体を読み取る方法です。
ファイルがリソースディレクトリにある場合、これを行うことができます。
(let [file-content-str (Slurp (clojure.Java.io/resource "public/myfile.txt")])
clojure.Java.io
を要求/使用することを忘れないでください。
(require '[clojure.Java.io :as io])
(io/copy (io/file "/etc/passwd") \*out*\)
ファイルを1行ずつ読み取るために、相互運用に頼る必要はなくなりました。
(->> "data.csv"
io/resource
io/reader
line-seq
(drop 1))
これは、データファイルがリソースディレクトリに保持され、最初の行が破棄可能なヘッダー情報であることを前提としています。