Существуют различные способы сделать это, в зависимости от того, что именно вы хотите.
Если у вас есть function
, который вы хотите применить к каждой строке в файле, вы можете использовать код, подобный ответу Абхинава:
(with-open [rdr ...]
(doall (map function (line-seq rdr))))
Преимущество заключается в том, что файл открывается, обрабатывается и закрывается настолько быстро, насколько это возможно, но заставляет весь файл использоваться одновременно.
Если вы хотите отложить обработку файла, у вас может возникнуть желание вернуть строки, но это не сработает :
(map function ; broken!!!
(with-open [rdr ...]
(line-seq rdr)))
потому что файл закрывается при возврате with-open
, то есть до , когда вы лениво обрабатываете файл.
Одним из способов решения этой проблемы является извлечение всего файла в память с помощью slurp
:
(map function (slurp filename))
Это имеет очевидный недостаток - использование памяти - но гарантирует, что вы не оставите файл открытым.
Альтернативой является оставить файл открытым, пока вы не доберетесь до конца чтения, генерируя ленивую последовательность:
(ns ...
(:use clojure.test))
(defn stream-consumer [stream]
(println "read" (count stream) "lines"))
(defn broken-open [file]
(with-open [rdr (clojure.java.io/reader file)]
(line-seq rdr)))
(defn lazy-open [file]
(defn helper [rdr]
(lazy-seq
(if-let [line (.readLine rdr)]
(cons line (helper rdr))
(do (.close rdr) (println "closed") nil))))
(lazy-seq
(do (println "opening")
(helper (clojure.java.io/reader file)))))
(deftest test-open
(try
(stream-consumer (broken-open "/etc/passwd"))
(catch RuntimeException e
(println "caught " e)))
(let [stream (lazy-open "/etc/passwd")]
(println "have stream")
(stream-consumer stream)))
(run-tests)
Какие отпечатки:
caught #<RuntimeException java.lang.RuntimeException: java.io.IOException: Stream closed>
have stream
opening
closed
read 29 lines
Показывает, что файл даже не открывался, пока не понадобился.
Этот последний подход имеет то преимущество, что вы можете обрабатывать поток данных «в другом месте», не сохраняя все в памяти, но он также имеет важный недостаток - файл не закрывается до тех пор, пока не будет прочитан конец потока. Если вы не будете осторожны, вы можете открыть много файлов параллельно или даже забыть закрыть их (не читая поток полностью).
Лучший выбор зависит от обстоятельств - это компромисс между ленивой оценкой и ограниченными системными ресурсами.
PS: lazy-open
определено где-нибудь в библиотеках? Я пришел к этому вопросу, пытаясь найти такую функцию, и закончил тем, что написал свою собственную, как указано выше.