Java IO PushbackReader example in Clojure

A PushbackReader can wrap a stream from file, string or standard input. PushbackReader is usually used to implement a parser which need to read chars one by one from a source and may need to pushback chars back to the stream or look ahead.

For example the Clojure programming language, when you working in a REPL, everything you input are sent to Coljure through System.in which then wrapped in a LineNumberingPushbackReader.

Create a PushbackReader

Let's define a PushbackReader on a String in Clojure

 
(def pbr (java.io.PushbackReader. (java.io.InputStreamReader. (java.io.ByteArrayInputStream. (.getBytes "hello\n"))) ) )
 

And see what's inside a PushbackReader

 
user> show java.io.PushbackReader
(#<Method public int java.io.PushbackReader.read() throws java.io.IOException>
 
#<Method public int java.io.PushbackReader.read(char[],int,int) throws java.io.IOException>
 
nil #<Method public void java.io.PushbackReader.close() throws java.io.IOException>
 
nil #<Method public void java.io.PushbackReader.mark(int) throws java.io.IOException>
 
nil #<Method public boolean java.io.PushbackReader.markSupported()>
 
nil #<Method public void java.io.PushbackReader.reset() throws java.io.IOException>
 
nil #<Method public long java.io.PushbackReader.skip(long) throws java.io.IOException>
 
nil #<Method private void java.io.PushbackReader.ensureOpen() throws java.io.IOException>
 
nil #<Method public boolean java.io.PushbackReader.ready() throws java.io.IOException>
 
nil #<Method public void java.io.PushbackReader.unread(char[]) throws java.io.IOException>
 
nil #<Method public void java.io.PushbackReader.unread(int) throws java.io.IOException>
 
nil #<Method public void java.io.PushbackReader.unread(char[],int,int) throws java.io.IOException>
 
nil nil)
 

Read a char from PushbackReader

The read method will retrieve a char from the stream and the stream move one char

 
user> (.read pbr)
104
user> (int \h)
104
user> 
 

The next call on read will return "e".

Pushback or unread a char to stream

Pushback a char will prepend to the front of the stream and move the stream one character backward.

 
(.unread pbr (int \a))
 

But if you unread it again, you will get overflow error

 
user> (.unread pbr (int \a))
IOException Pushback buffer overflow  java.io.PushbackReader.unread (PushbackReader.java:155)
 

Why? Because by default the pushback buffer has length of one. Every time you execute unread, the pushback buffer is consumed and full, but one or more read will free the buffer.

Read and push back a string to stream

Sometime you may want to read or push back a whole string to stream, you need to set the push back buffer size larger than 1.

 
(def pbr (java.io.PushbackReader. (java.io.InputStreamReader. (java.io.ByteArrayInputStream. (.getBytes "hello\n"))) 100) )
 

Read the whole string

 
  (clojure.string/reverse 
    (loop [ch (.read pbr) acc ""]
      (if (= ch -1)
        acc
        (recur (.read pbr) (str (char ch) acc))
      )
    )
  )
 

-1 means the EOF, if you want to read a line each time, set it to \newline

And push it back

 
(.unread pbr (char-array (str "hello, world")))