Clojure 1.3에서 파일을 읽고 쓰는 방법


163

clojure 1.3에서 파일을 읽고 쓰는 "추천 된"방법을 알고 싶습니다.

  1. 전체 파일을 읽는 방법
  2. 파일을 한 줄씩 읽는 방법
  3. 새 파일을 작성하는 방법
  4. 기존 파일에 줄을 추가하는 방법

2
구글의 첫 번째 결과 : lethain.com/reading-file-in-clojure
jcubic

8
이 결과는 2009 년 이후로 최근 일부 변경되었습니다.
Sergey 11 :

11
과연. 이 StackOverflow 질문은 이제 Google의 첫 번째 결과입니다.
mydoghasworms

답변:


273

우리가 여기서 이진 파일이 아닌 텍스트 파일 만하고 있다고 가정합니다.

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 등도 가능)을로 강제 변환합니다 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는 BufferedWriterjava 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)

추신 : 다른 것을 읽지 않고 파일을 읽고 쓰는 사실에 대해 더 명확하게 설명하기 위해 먼저 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)

1
자세한 답변을 주셔서 대단히 감사합니다. 1.3에서 권장되는 File IO (텍스트 파일) 방법을 알게되어 기쁩니다. File IO에 대한 라이브러리 (clojure.contrb.io, clojure.contrib.duck-streams 및 Java BufferedReader FileInputStream InputStreamReader 직접 사용하는 일부 예제)에 대한 라이브러리가있어서 더 혼란 스럽습니다. 또한 Clojure 1.3에 대한 정보는 거의 없습니다. 특히 일본어 (내 자연어)입니다. 감사합니다.
jolly-san

내 대답을 수락 해 주셔서 감사합니다. 귀하의 정보를 위해 clojure.contrib.duck-streams는 더 이상 사용되지 않습니다. 이것은 아마도 혼란을 가중시킵니다.
Michiel Borkent

즉, 라인 모음 대신 (with-open [rdr (reader "/tmp/test.txt")] (line-seq rdr))수율 IOException Stream closed입니다. 무엇을해야합니까? 그러나 @satyagraha의 답변으로 좋은 결과를 얻고 있습니다.
0dB

4
이것은 게으름과 관련이 있습니다. 결과를 REPL에 인쇄 할 때 발생하는 with-open 외부에서 line-seq 결과를 사용하면 판독기가 이미 닫힙니다. 해결책은 doll 안에 line-seq를 감아 서 평가를 즉시 강제하는 것입니다. (with-open [rdr (reader "/tmp/test.txt")] (doall (line-seq rdr)))
Michiel Borkent

나와 같은 실제 초보자의 경우 반환 값이 반환 값이 슬픈 doseq반환 시간 nil이 될 수 있습니다.
10

33

파일이 메모리에 맞는 경우 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 문에서 열린 상태로 유지되고 읽기 및 쓰기가 버퍼링되므로 파일을 반복해서 읽거나 쓸 때 더 효율적입니다.

자세한 내용은 다음과 같습니다. slurp spit clojure.java.io Java의 BufferedReader Java의 작성기


1
폴 감사합니다. 귀하의 코드와 귀하의 의견으로 더 많은 것을 배울 수 있습니다. 대단히 감사합니다.
jolly-san

일반적인 경우에 가장 적합한 방법에 대한 Michiel Borkent의 (우수한) 답변에 나와 있지 않은 약간 낮은 수준의 방법에 대한 정보를 추가해 주셔서 감사합니다.
화성

@ 화성 감사합니다. 실제로 나는이 질문에 먼저 대답했지만 Michiel의 대답은 더 많은 구조를 가지고 있으며 매우 인기가있는 것 같습니다.
Paul

그는 일반적인 경우에 대해 잘 수행하지만 다른 정보를 제공했습니다. 그렇기 때문에 SE가 여러 답변을 허용하는 것이 좋습니다.
화성

6

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

7
중첩 defn이 이데올로기적인 Clojure 라고 생각하지 않습니다 . 여러분 read-next-line, 지금까지의 내가 이해, 당신의 볼 수 밖에 read-lines기능. (let [read-next-line (fn [] ...))대신 사용했을 수 있습니다 .
kristianlm

전역 바인딩이 아닌 생성 된 함수 (공개 리더를 닫음)를 반환하면 답변이 약간 더 좋을 것이라고 생각합니다.
Ward

1

전체 파일을 읽는 방법입니다.

파일이 자원 디렉토리에있는 경우 다음을 수행 할 수 있습니다.

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

필요 / 사용을 기억하십시오 clojure.java.io.


0
(require '[clojure.java.io :as io])
(io/copy (io/file "/etc/passwd") \*out*\)

0

파일을 한 줄씩 읽으려면 더 이상 interop을 사용하지 않아도됩니다.

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

이는 데이터 파일이 resources 디렉토리에 유지되고 첫 번째 행은 버릴 수있는 헤더 정보라고 가정합니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.