ftravers
ftravers

Reputation: 3999

read a file into a list, each element represents one line of the file

In clojure, how do I read a file into a sequence where each line is one element in the sequence. So I'd like to see the definition of the function get-lines so I could do the following:

(def lines (get-lines "test.txt"))

and lines is a non-lazy sequence.

Upvotes: 6

Views: 7570

Answers (3)

Retief
Retief

Reputation: 3217

Alternate implementation:

(require ['clojure.string :as 'str])

(defn get-lines [file]
  (str/split-lines (slurp file)))

This function returns a vector of the results instead of a seq.

If you are not using 1.3, require clojure.contrib.string instead.

Upvotes: 12

Jonas
Jonas

Reputation: 19642

You could use line-seq. A quick example:

(ns your.project
  (:require [clojure.java.io :as io]))

(defn count-lines [filename]
  (with-open [rdr (io/reader filename)]
    (count (line-seq rdr))))

Note that line-seq is lazy. You must be careful not to consume the sequence after the reader is closed. The following will not work:

(def lines (with-open [rdr (io/reader "some-file")]
             (line-seq rdr)))

(println (first lines))

The first example works because count isn't lazy.

If you want to do something (with side effects) with the lines you'll probably find doseq most useful:

;; copy every "short" line in file
(with-open [rdr (io/reader from)
            wrt (io/writer to)]
  (binding [*out* wrt]
    (doseq [line (line-seq rdr) :when (< (count line) 10)]
      (println line))))

Upvotes: 2

MisterMetaphor
MisterMetaphor

Reputation: 6008

To convert lazy sequence to non-lazy you can use doall like so:

(use 'clojure.java.io)

(defn get-lines [fname]
  (with-open [r (reader fname)]
    (doall (line-seq r))))

Upvotes: 10

Related Questions