ฉันต้องการทราบวิธีการ "แนะนำ" ในการอ่านและเขียนไฟล์ใน Clojure 1.3
- วิธีอ่านไฟล์ทั้งหมด
- วิธีอ่านไฟล์ทีละบรรทัด
- วิธีเขียนไฟล์ใหม่
- วิธีเพิ่มบรรทัดลงในไฟล์ที่มีอยู่
ฉันต้องการทราบวิธีการ "แนะนำ" ในการอ่านและเขียนไฟล์ใน Clojure 1.3
คำตอบ:
สมมติว่าเราทำไฟล์ข้อความที่นี่เท่านั้น
หมายเลข 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
แมโครจะดูแลว่าผู้อ่านจะปิดท้ายของร่างกาย ฟังก์ชั่นอ่าน coerces สตริง (มันยังสามารถทำ URL ที่ ฯลฯ ) BufferedReader
เป็นline-seq
ส่ง 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 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:เพื่อให้ชัดเจนยิ่งขึ้นเกี่ยวกับความจริงที่ว่าคุณกำลังอ่านและเขียนลงในไฟล์และไม่ใช่อย่างอื่นคุณสามารถสร้างวัตถุไฟล์ก่อนแล้วจึงบีบบังคับให้เป็นBufferedReader
หรือนักเขียน:
(reader (file "/tmp/test.txt"))
;; or
(writer (file "tmp/test.txt"))
ฟังก์ชั่นไฟล์ยังอยู่ใน clojure.java.io
PS2:บางครั้งมันเป็นประโยชน์ที่จะสามารถดูว่าไดเรกทอรีปัจจุบัน (ดังนั้น ".") คืออะไร คุณสามารถหาพา ธ สัมบูรณ์ได้สองวิธี:
(System/getProperty "user.dir")
หรือ
(-> (java.io.File. ".") .getAbsolutePath)
(with-open [rdr (reader "/tmp/test.txt")] (line-seq rdr))
อัตราผลตอบแทนIOException Stream closed
แทนการสะสมของเส้น จะทำอย่างไร? ฉันได้รับผลลัพธ์ที่ดีพร้อมคำตอบโดย @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)
หากต้องการอ่านหรือเขียนไฟล์ linewise คุณจะต้องใช้ตัวอ่านและตัวเขียนของ Java พวกเขาถูกห่อใน namespace 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) ในภายหลังและการอ่านและการเขียนจะถูกบัฟเฟอร์ดังนั้นจึงมีประสิทธิภาพมากขึ้นเมื่ออ่านจาก / เขียนไปยังไฟล์ซ้ำ ๆ
นี่คือข้อมูลเพิ่มเติม: slurp spit clojure.java.io Java's BufferedReader Java'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)))
defn
เป็น Clojure ในอุดมคติ ของคุณread-next-line
เท่าที่ผมเข้าใจอยู่นอกที่มองเห็นของคุณread-lines
ฟังก์ชั่น คุณอาจใช้(let [read-next-line (fn [] ...))
แทน
นี่คือวิธีการอ่านไฟล์ทั้งหมด
หากไฟล์อยู่ในไดเรกทอรีทรัพยากรคุณสามารถทำได้:
(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*\)
หากต้องการอ่านไฟล์ทีละบรรทัดคุณไม่จำเป็นต้องหันมาใช้ interop อีกต่อไป:
(->> "data.csv"
io/resource
io/reader
line-seq
(drop 1))
นี่ถือว่าไฟล์ข้อมูลของคุณถูกเก็บไว้ในไดเรกทอรีทรัพยากรและบรรทัดแรกคือข้อมูลส่วนหัวที่สามารถละทิ้งได้