10

I want to read first n lines from a file using clojure. Here is my code:

(defn read-nth-line [file]
  (with-open [rdr (reader file)]
    (loop [line-number 0]
      (when (< line-number 20)
            (nth (line-seq rdr) line-number)
            (recur (inc line-number))))))

but when I run

 user=> (read-nth-line "test.txt")

 IndexOutOfBoundsException   clojure.lang.RT.nthFrom (RT.java:871)

I have no idea why I got such an error.

ntalbs
  • 28,700
  • 8
  • 66
  • 83
Xiufen Xu
  • 531
  • 1
  • 3
  • 19

1 Answers1

20

Your code produces an out-of-bounds error because you call line-seq multiple times on the same reader. If you want to get a number of lines from a reader, you should call line-seq only once, then take the desired number of lines from that sequence:

(require '[clojure.java.io :as io])

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

Example:

(run! println (lines 20 "test.txt"))

If test.txt contains fewer than 20 lines, this will simply print all the lines in the file.

Sam Estep
  • 12,974
  • 2
  • 37
  • 75
  • Can you add the require to your answer? I like being able to copy paste code into the REPL (require '[clojure.java.io :refer [reader]]) – Michiel Borkent Apr 12 '16 at 18:31
  • 1
    @MichielBorkent Sure; I was just trying to mirror the OP's style. I added a `require` with an alias, because I prefer to use `:as` over `:refer` when feasible. – Sam Estep Apr 12 '16 at 19:55