Java PushbackReader
Jakob Jenkov |
The Java PushbackReader class, java.io.PushbackReader
, is intended to be used when you
parse data from a Reader. Sometimes you need to read ahead a few characters to see what is coming,
before you can determine how to interpret the current character. The Java PushbackReader
allows you to do
that. Well, actually it allows you to push back the read characters into the Reader
. These characters
will then be read again the next time you call read()
.
The Java PushbackReader
works much like the PushbackInputStream
except that the PushbackReader
works on characters, whereas the PushbackInputStream
works
on bytes.
PushbackReader Example
Here is a simple PushbackReader
example:
PushbackReader pushbackReader = new PushbackReader(new FileReader("c:\\data\\input.txt")); int data = pushbackReader.read(); pushbackReader.unread(data);
The call to read()
reads a character from the PushbackReader
just like from
any other Reader
. The call to unread()
pushes a character back into the
PushbackReader
. The next time read()
is called the pushed back characters will be read
first. If you push back multiple characters into the PushbackReader
, the latest pushed back character
will be returned first from the read()
method, just like with a stack.
Create a PushbackReader
To use a Java PushbackReader
you must first create a PushbackReader
instance.
You create a PushbackReader
using standard object instantiation - using the new
operator. To the PushbackReader
constructor you must pass a Java Reader
from which the PushbackReader
will read its characters. Here is an example of creating a
Java PushbackReader
:
PushbackReader pushbackReader = new PushbackReader(new FileReader("c:\\data\\input.txt"));
This example passes a Java FileReader to the PushbackReader
constructor.
This will make the PushbackReader
read its characters from this FileReader
.
Setting the Push Back Limit of a PushbackReader
You can set the number of characters you should be able to unread in the constructor of
the PushbackReader
. Here is how to set the pushback limit using the
PushbackReader
constructor:
int pushbackLimit = 8; PushbackReader reader = new PushbackReader( new FileReader("c:\\data\\input.txt"), pushbackLimit);
This example sets an internal buffer of 8 characters in the PushbackReader
. That means you can
unread at most 8 characters at a time, before reading them again.
Read Characters
You read characters from a Java PushbackReader
just like you do from a Java Reader
-
because PushbackReader
is a Java Reader
subclass. In other words, you use its
read()
method which is inherited from the Reader
class. Here is an example of
reading characters from a Java PushbackReader
via its read()
method:
int aChar = pushbackReader.read(); while(aChar != -1) { System.out.println((char) aChar); aChar = pushbackReader.read(); }
The read()
returns an int
which you will have to cast to a char
yourself,
as shown in the example above. When there are no characters available in the PushbackReader
the
read()
method will return the int
value -1.
Push Back Characters
To push a character back into a Java PushbackReader
you must call its unread()
method.
Here is an example of pushing back a character into a PushbackReader
:
int aChar = pushbackReader.read(); pushbackReader.unread(aChar); aChar = pushbackReader.read(); // reads character pushed back
This example reads a character from a PushbackReader
, then pushes it back into the
PushbackReader
, and then reads that character from the PushbackReader
again.
Closing a PushbackReader
When you are finished reading characters from the PushbackReader
you should remember to close it.
Closing a PushbackReader
will also close the Reader
instance from which the
PushbackReader
is reading.
Closing a PushbackReader
is done by calling its close()
method. Here is how
closing a PushbackReader
looks:
pushbackReader.close();
You can also use the try-with-resources construct
introduced in Java 7. Here is how to use and close a PushbackReader
looks with the try-with-resources
construct:
Reader reader = new FileReader("data/data.bin"); try(PushbackReader pushbackReader = new PushbackReader(reader)){ int data = pushbackReader.read(); while(data != -1) { System.out.print((char) data); data = pushbackReader.read(); } }
Notice how there is no longer any explicit close()
method call. The try-with-resources construct
takes care of that.
Notice also that the first FileReader
instance is not created inside
the try-with-resources block. That means that the try-with-resources block will not automatically close this
FileReader
instance. However, when the PushbackReader
is closed
it will also close the Reader
instance it reads from, so the FileReader
instance will get closed when the PushbackReader
is closed.
Parsing Example
At the end of this Java PusbackReader
tutorial, lets see a slightly more elaborate example of
how you can use the PushbackReader
when parsing a stream of characters. This example samples
the first character of the next "token" to be read from a PushbackReader
to determine the type
of the next token, then pushes back the character for the corresponding tokenizer to read. The example is a bit
"constructed" for the occasion, meaning in a real parser you might do things a bit differently. However, the
example serves mostly to show how the PushbackReader
can be used in a real parsing example, not
as a text book example of writing great parsers.
public class TextTokenizer { protected PushbackReader pushbackReader = null; public TextTokenizer(Reader reader) { this.pushbackReader = new PushbackReader(reader); } public String nextToken() { int firstChar = this.pushbackReader.read(); this.pushbackReader.unread(firstChar); if(((char)firstChar) == '"') { return readDoubleQuotedToken(); } if((char)firstChar) == '\'') { return readSingleQuotedToken(); } return readSingleWordToken(); } protected String readDoubleQuotedToken() { ... } protected String readSingleQuotedToken() { ... } protected String readSingleWordToken() { ... } }
The interesting part of this example is the nextToken()
method. This method first reads a character
from the PushbackReader
into a variable, then pushes the read character back into the PushbackReader
.
This way the nextToken()
method can "sample" the first character of the next token, and based on that
decide what kind of token it is, and what read method to call for that kind of token.
Note, that for the single and double quoted tokens it would actually not be necessary to push the character back
into the PushbackReader
, because the quotes themselves are typically not included as part of the token.
For the readSingleTokenWord()
however, it is necessary, as the character read is the first character
of the token's value.
The implementations of the readDoubleQuotedToken()
, readSingleQuotedToken()
and
readSingleWordToken()
have been left out to keep the example short. Just imagine they read
a token enclosed by double quotes ("), single quotes (') or a token which ends with a non-word character
(e.g. a space, tab, line break etc.).
Tweet | |
Jakob Jenkov |