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











