How to read lines from stdin (*in*) in clojure

17,822

Solution 1

Try wrapping *in* in a java.io.BufferedReader. And also use doseq instead of doall, as devstopfix pointed out:

(doseq [ln (line-seq (java.io.BufferedReader. *in*))]
   (println ln))

Note that line-seq is documented to require a BufferedReader as its source.

Solution 2

Just a note that for anyone who wants to only read a single line, there's the read-line function.

Solution 3

You should probably use doseq instead of doall:

(doseq [line (line-seq (java.io.BufferedReader. *in*))] 
    (println line))

doall:

Walks through the successive nexts of the seq, retains the head and returns it, thus causing the entire seq to reside in memory at one time.

doseq:

Does not retain the head of the sequence. Returns nil.

Solution 4

For reasonably small inputs, the following would also work:

(let [input-string (slurp *in*)]
  (println input-string))

Or, splitting by lines:

(let [lines (clojure.string/split-lines (slurp *in*))]
  (println lines))
Share:
17,822
Dave Kirby
Author by

Dave Kirby

Updated on June 21, 2022

Comments

  • Dave Kirby
    Dave Kirby almost 2 years

    I am writing my first clojure program, and want to read lines from stdin.

    When I try this:

    (doall (map #(println %) (line-seq *in*)))
    

    I get this exception:

    Exception in thread "main" java.lang.ClassCastException: clojure.lang.LineNumberingPushbackReader cannot be cast to java.io.BufferedReader (test.clj:0)
    

    I get the same results in version 1.0 and 1.1

    So how do I convert *in* into a seq I can iterate over? I would have thought that this is common enough that *in* itself would be iterable, but that does not work either - if I try to use it directly I get:

    java.lang.IllegalArgumentException: Don't know how to create ISeq from: clojure.lang.LineNumberingPushbackReader (NO_SOURCE_FILE:0)
    

    Also, are there any examples of doing general file handling in clojure?

    • GabiMe
      GabiMe over 14 years
      replacing (map #(println %) (line-seq.. )) with just (map println (line-seq..)) could be nice
  • Jason
    Jason over 9 years
    This code does not work. I get the error "doseq requires a vector for its binding" Try was @devstopfix wrote. It worked fro me.
  • user1009908
    user1009908 almost 9 years
    @Jason Use dorun instead of doseq for this form. doseq requires a binding form. dorun forces a lazy seq.
  • Alexandre Nascimento
    Alexandre Nascimento over 4 years
    How can use a escape for this loop?
  • Eric Ihli
    Eric Ihli about 4 years
    What is "reasonably small"? Why is this only appropriate for "reasonably small" inputs?
  • Mars
    Mars about 2 years
    @EricIhli, slurp reads all of the input that's available (until EOF, i.e. end of file). So if there's a lot of input--at least many megabytes or several gigabytes, let's say--then you're loading all of into memory at the same time in a single variable input-string. And then after that, you're printing all of it out at once. That will work if you have enough RAM in your system and if you've started Clojure with Java options that tell Java to use enough RAM, but you still might have to wait awhile before you start seeing the printed output.