Java BufferedReader
Jakob Jenkov |
The Java BufferedReader class, java.io.BufferedReader, provides buffering for your
Java Reader instances. Buffering can speed up IO quite a bit. Rather than read one character at a time from the
underlying Reader, the Java BufferedReader reads a larger block (array) at a time. This is typically much faster,
especially for disk access and larger data amounts.
The Java BufferedReader is similar to the BufferedInputStream
but they are not exactly the same. The main difference between BufferedReader and
BufferedInputStream is that BufferedReader reads characters (text), whereas the
BufferedInputStream reads raw bytes.
The Java BufferedReader class is a subclass of the Java Reader class,
so you can use a BufferedReader anywhere a Reader is required.
Java BufferedReader Example
To add buffering to a Java Reader instance, simply wrap it in a BufferedReader.
Here is how that looks:
BufferedReader bufferedReader = new BufferedReader(
new FileReader("c:\\data\\input-file.txt"));
This example creates a BufferedReader which wraps a FileReader. The
BufferedReader will read a block of characters from the FileReader (typically into a
char array). Each character returned from read() is thus returned from this internal
array. When the array is fully read the BufferedReader reads a new block of data into the array etc.
BufferedReader Buffer Size
You can set the buffer size to use internally by the BufferedReader.
You provide the size as a constructor parameter, like this:
int bufferSize = 8 * 1024;
BufferedReader bufferedReader = new BufferedReader(
new FileReader("c:\\data\\input-file.txt"),
bufferSize
);
This example sets the internal buffer to 8 KB. It is best to use buffer sizes that are multiples of 1024 bytes. That works best with most built-in buffering in hard disks etc.
Except for adding buffering to Reader instances, a Java BufferedReader behaves pretty much
like a Reader. The BufferedReader has one extra method though, the readLine() method. This
method can be handy if you need to read input one line at a time. Here is a BufferedReader readLine()
example:
String line = bufferedReader.readLine();
The readLine() method will return a textual line (all text until at line break is found) read from
the BufferedReader. If there is no more data to read from the underlying Reader,
then the BufferedReader's readLine() method will return null .
Read Characters From a BufferedReader
The read() method of a Java BufferedReader returns an int which contains the char value of the next
character read. If the read() method returns -1, there is no more data to read in the BufferedReader,
and it can be closed. That is, -1 as int value, not -1 as byte or char value. There is a difference here!
Here is an example of reading all characters from a Java BufferedReader:
Reader reader =
new BufferedReader(
new FileReader("/path/to/file/thefile.txt"));
int theCharNum = reader.read();
while(theCharNum != -1) {
char theChar = (char) theCharNum;
System.out.print(theChar);
theCharNum = reader.read();
}
Notice how the code example first reads a single character from the Java BufferedReader and checks if
the char numerical value is equal to -1. If not, it processes that char and continues reading
until -1 is returned from the BufferedReader read() method.
As mentioned earlier, the BufferedReader will actually read an array of characters from the underlying
Reader and return these characters one by one, rather than forwarding every read() call
to the underlying Reader. When all characters in the internal buffer have been read, the
BufferedReader attempts to refill the buffer again, until no more characters can be read from the
underlying Reader.
Read Array of Characters From BufferedReader
The Java BufferedReader class also has a read() method that takes a char array
as parameter, as well as a start offset and length. The char array is where the read()
method will read the characters into. The offset parameter is where in the char array the read()
method should start reading into. The length parameter is how many characters the read() method
should read into the char array from the offset and forward. Here is an example of reading
an array of characters into a char array with a Java BufferedReader:
Reader reader =
new BufferedReader(
new FileReader("/path/to/file/thefile.txt"));
char[] theChars = new char[128];
int charsRead = reader.read(theChars, 0, theChars.length);
while(charsRead != -1) {
System.out.println(new String(theChars, 0, charsRead));
charsRead = reader.read(theChars, 0, theChars.length);
}
The read(char[], offset, length) method returns the number of characters read into the
char array, or -1 if there are no more characters to read in the BufferedReader, for instance
if the end of the file the BufferedReader is connected to has been reached.
Read a Line From BufferedReader
The Java BufferedReader has a special read method named readLine() which reads a
full line of text from the BufferedReader's internal buffer. The readLine() method
returns a String. If there are no more lines to read from the BufferedReader,
the readLine() method returns null. Here is an example of reading the lines of
a text file one by one using a Java BufferedReader :
BufferedReader bufferedReader =
new BufferedReader(
new FileReader("/path/to/file/thefile.txt"));
String line = bufferedReader.readLine();
while(line != null) {
System.out.println(line);
line = bufferedReader.readLine();
}
Read Performance
Reading an array of characters at a time is faster than reading a single character at a time from a
Java Reader. However, since the BufferedReader does some internal buffering already,
the difference is most likely not as dramatic as with a Reader that uses no buffering.
You will most likely still see a small difference though.
Skip Characters
The Java BufferedReader class has a method named skip() which can be used to skip over
a number of characters in the input that you do not want to read. You pass the number of characters to
skip as parameter to the skip() method. Here is an example of skipping characters from
a Java BufferedReader :
long charsSkipped = bufferedReader.skip(24);
This example tells the Java BufferedReader to skip over the next 24 characters in the BufferedReader.
The skip() method returns the actual number of characters skipped. In most cases that will be
the same number as you requested skipped, but in case there are less characters left in the BufferedReader
than the number you request skipped, the returned number of skipped characters can be less than the number
of characters you requested skipped.
Closing a BufferedReader
When you are finished reading characters from the BufferedReader you should remember to close it.
Closing a BufferedReader will also close the Reader instance from which the
BufferedReader is reading.
Closing a BufferedReader is done by calling its close() method. Here is how
closing a BufferedReader looks:
bufferedReader.close();
You can also use the try-with-resources construct
introduced in Java 7. Here is how to use and close a BufferedReader looks with the try-with-resources
construct:
Reader reader = new FileReader("data/data.bin");
try(BufferedReader bufferedReader =
new BufferedReader(reader)){
String line = bufferedReader.readLine();
while(line != null) {
//do something with line
line = bufferedReader.readLine();
}
}
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 BufferedReader is closed
it will also close the Reader instance it reads from, so the FileReader
instance will get closed when the BufferedReader is closed.
Reusable BufferedReader
One of the weaknesses of the standard Java BufferedReader is that it can only be used once.
Once you close it, it's no longer usable. If you need to read a lot of files, or network streams, you have to create
a new BufferedReader for each file or network stream you want to read. This means that you are creating a
new object, and more importantly, a new char array which is used as buffer inside the BufferedReader.
This can put pressure on the Java garbage collector, if the number of files or streams read is high, and if they
are read quickly after each other.
An alternative is to create a reusable BufferedReader where you can replace the underlying source
Reader, so the BufferedReader and its internal byte array buffer can be reused. To save
you the trouble, I have created such a ReusableBufferedReader, and included the code for it
further down this tutorial. First, I want to show you how using this ReusableBufferedReader looks.
Create a ReusableBufferedReader
First you need to create a ReusableBufferedReader. Here is an example of how to create a
ReusableBufferedReader:
ReusableBufferedReader reusableBufferedReader =
new ReusableBufferedReader(new char[1024 * 1024]);
This example creates a ReusableBufferedReader with a 2 MB char array (1024 * 1024 chars, 1 char = 2 bytes)
as its internal buffer.
Set Source
When you have created a ReusableBufferedReader you need to set the Reader
on it to use as underlying data source. Here is how you set a source Reader on a
ReusableBufferedReader :
FileReader reader = new FileReader("/mydata/somefile.txt");
reusableBufferedReader.setSource(reader);
The setSource() method actually returns a reference to the ReusableBufferedReader,
so you can actually create a ReusableBufferedReader and set the source in a single instruction:
ReusableBufferedReader reusableBufferedReader =
new ReusableBufferedReader(new byte[1024 * 1024])
.setSource(new FileReader("/mydata/somefile.txt"));
Reusing a ReusableBufferedReader
When you are done using the ReusableBufferedReader you need to close it. Closing it will only
close the underlying source Reader. After closing a ReusableBufferedReader
you can use it again, simply by setting a new source Reader on it. Here is how it looks to
reuse a ReusableBufferedReader :
reusableBufferedReader.setSource(new FileReader("/mydata/file-1.txt"));
//read data from ReusableBufferedReader
reusableBufferedReader.close();
reusableBufferedReader.setSource(new FileReader("/mydata/file-1.txt"));
//read data from ReusableBufferedReader
reusableBufferedReader.close();
ReusableBufferedReader Code
Here is the code for the ReusableBufferedReader described above. Note, that this implementation
only overrides the read() and read(char[] dest, int offset, int length) methods of
the Reader class that it extends. The rest of the
Reader methods have been left out to keep the code shorter - but you can implement them yourself
in case you need them.
import java.io.IOException;
import java.io.Reader;
public class ReusableBufferedReader extends Reader {
private char[] buffer = null;
private int writeIndex = 0;
private int readIndex = 0;
private boolean endOfReaderReached = false;
private Reader source = null;
public ReusableBufferedReader(char[] buffer) {
this.buffer = buffer;
}
public ReusableBufferedReader setSource(Reader source){
this.source = source;
this.writeIndex = 0;
this.readIndex = 0;
this.endOfReaderReached = false;
return this;
}
@Override
public int read() throws IOException {
if(endOfReaderReached) {
return -1;
}
if(readIndex == writeIndex) {
if(writeIndex == buffer.length) {
this.writeIndex = 0;
this.readIndex = 0;
}
//data should be read into buffer.
int bytesRead = readCharsIntoBuffer();
while(bytesRead == 0) {
//continue until you actually get some bytes !
bytesRead = readCharsIntoBuffer();
}
//if no more data could be read in, return -1;
if(bytesRead == -1) {
return -1;
}
}
return 65535 & this.buffer[readIndex++];
}
@Override
public int read(char[] dest, int offset, int length) throws IOException {
int charsRead = 0;
int data = 0;
while(data != -1 && charsRead < length){
data = read();
if(data == -1) {
endOfReaderReached = true;
if(charsRead == 0){
return -1;
}
return charsRead;
}
dest[offset + charsRead] = (char) (65535 & data);
charsRead++;
}
return charsRead;
}
private int readCharsIntoBuffer() throws IOException {
int charsRead = this.source.read(this.buffer, this.writeIndex, this.buffer.length - this.writeIndex);
writeIndex += charsRead;
return charsRead;
}
@Override
public void close() throws IOException {
this.source.close();
}
}
| Tweet | |
Jakob Jenkov | |











