clojure 1.3でファイルを読み書きするための「推奨」方法を教えてください。
- ファイル全体を読み取る方法
- ファイルを1行ずつ読み取る方法
- 新しいファイルを書く方法
- 既存のファイルに行を追加する方法
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
レイジーシーケンスを提供します。レイジーシーケンスの次の要素を要求すると、リーダーから行が読み取られます。
Clojure 1.7以降では、テキストファイルの読み取りにトランスデューサーを使用することもできます。
番号3:新しいファイルへの書き込み方法。
(use 'clojure.java.io)
(with-open [wrtr (writer "/tmp/test.txt")]
(.write wrtr "Line to be written"))
ここでも、with-open
がBufferedWriter
本体の端で閉じていることに注意してください。Writerは文字列をに変換します。これは、BufferedWriter
Java相互運用機能を介して使用します。(.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"))
file関数もclojure.java.ioにあります。
PS2:ときどき、現在のディレクトリ(「。」)が何であるかを確認できると便利です。絶対パスを取得するには、次の2つの方法があります。
(System/getProperty "user.dir")
または
(-> (java.io.File. ".") .getAbsolutePath)
(with-open [rdr (reader "/tmp/test.txt")] (line-seq rdr))
はIOException Stream closed
なく、yields です。何をすべきか?ただし、@ satyagrahaの回答で良い結果が得られています。
(with-open [rdr (reader "/tmp/test.txt")] (doall (line-seq rdr)))
doseq
リターンはnil
悲しい無返還価値の時間につながる可能性があることに注意してください。
ファイルがメモリに収まる場合は、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ステートメントで)開いたままであり、読み取りと書き込みがバッファリングされるため、ファイルの読み取りと書き込みを繰り返し行う場合により効率的です。
ここではより多くの情報がある:吸い込み 串 clojure.java.io JavaのBufferedReaderの Javaのライター
質問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 [] ...))
代わりに使用した可能性があります。