Dave Kirby
Dave Kirby

Reputation: 26552

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

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?

Upvotes: 44

Views: 19950

Answers (5)

Murilo Perrone
Murilo Perrone

Reputation: 504

You might want to read the input line by line, allowing other parts of your code to pull lines from it as well. Having this in mind, I came to this improvement over previous answers.

(doseq [ln (take-while some? (repeatedly read-line))]
    (println ln)
)

And in case you want a complete input reading example, here is one:

(require '[clojure.string :as str])
(doseq [ln (take-while some? (repeatedly read-line))]
    (def parts (map read-string (str/split ln #" ")))
    (def ah (nth parts 0))
    (def am (nth parts 1))
    (def h (quot (* 12 ah) 360))
    (def m (quot (* 60 am) 360))
    (println (format "%02d:%02d" h m))
)

The above example reads lines from the input, each line containing a pair of integers representing the angle of clock pointer (0 to 360), then calculates and prints the time for each clock pictured. You can actually try the code in beecrowd online-judge here, or may try replit.com creating an input and running it with clojure -M main.clj < input.txt.

Upvotes: 0

mrucci
mrucci

Reputation: 4470

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

Upvotes: 10

seh
seh

Reputation: 15259

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.

Upvotes: 39

devstopfix
devstopfix

Reputation: 6798

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.

Upvotes: 25

Mike Crittenden
Mike Crittenden

Reputation: 5917

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

Upvotes: 25

Related Questions