In the previous section, we showed how to read and write characters using the FileReader and FileWriter classes.

But in practice, a lot of file reading and writing will be of character data delimited by line breaks. Java provides two classes for reading and writing character data a line at a time: BufferedReader and BufferedWriter, which are subclasses of Reader and Writer.

Recall what we said about data being nothing more than a stream of characters whose line and/or field boundaries are determined by the software reading or writing them. BufferedReader performs the service of determining the end of a line of text on input by breaking the stream into chunks between end-of-line and/or newline characters (without you having to worry about whether only one or both are present). BufferedWriter provides a method for terminating a line of text according to the platform the code is running on.

To read a text file line by line, you introduce a BufferedReader like so:

// Create the input File object.
File inputFile = new File("/com/mycompany/myproject/input.txt");

// Create a Reader for the input File.
// Reader is abstract; FileReader is the concrete class.
Reader rdr = new FileReader(inputFile);

// Add a BufferedReader.
BufferedReader bufRdr = new BufferedReader(rdr);

// Establish a String to receive the data.
String buffer = "";

// We'll read from the file until we run out of data, as
// signified by buffer being null. Note the syntax
// that accomplishes reading into the buffer AND testing
// the result.
try {
    while ((buffer = bufRdr.readLine()) != null) {
        // do something with the data here
    }
} finally {
    bufRdr.close();
    rdr.close();
}

And now let’s add code to write to the output file.

// Create the input File object.
File inputFile = new File("/com/mycompany/myproject/input.txt");

// Create a Reader for the input File.
// Reader is abstract; FileReader is the concrete class.
Reader rdr = new FileReader(inputFile);

// Add a BufferedReader.
BufferedReader bufRdr = new BufferedReader(rdr);

// Establish a String to receive the data.
String buffer = "";

// Create the output File object.
File outputFile = new File("/com/mycompany/myproject/output.txt");

// Create a Writer for the output file.
Writer wtr = new FileWriter(outputFile);

// Add a BufferedWriter.
BufferedWriter bufWtr = new BufferedWriter(wtr);

// Establish a String to receive the data.
String buffer = "";

// We'll read from the file until we run out of data, as
// signified by buffer being null. Note the syntax
// that accomplishes reading into the buffer AND testing
// the result.
try {
    while ((buffer = bufRdr.readLine()) != null) {
        bufWtr.write(buffer);
        bufWtr.newLine();
        bufWtr.flush();
    }
} finally {
    bufRdr.close();
    rdr.close();
    bufWtr.close();
    wtr.close();
}

The newLine() method is important here. It writes an end-of-line sequence in Windows format if you’re running on Windows, and in Unix format otherwise. This is one more service of BufferedWriter. If you don’t call newLine(), you have to determine which kind of line break to use and write them yourself, or there won’t be any. newLine() is way easier.

LineNumberReader

We mustn’t forget the LineNumberReader class, a subclass of BufferedReader, which keeps track of how many lines have been read from the input. LineNumberReader returns this value from the getLineNumber() method. You can also call setLineNumber() to reset the line number tally. Not without its uses, I suppose.

Exercise

This is the same as the exercise on the previous page, but this time use BufferedReader and BufferedWriter.

Click here for a solution. Click here for a refresher on importing projects. The solution contains the input file, and writes the output file within the project’s workspace. After running, you’ll need to refresh the Package Explorer in Eclipse to see the output file. Right-click on the project node, and select Refresh from the popup menu or click F5.

The input file for the solution happens to use Unix end-of-line. If you’re running in Windows, you’ll notice that the output file uses Windows end-of-line. Notepad++ can confirm this.

What You Need to Know

  • BufferedReader and BufferedWriter supplement FileReader and FileWriter to make it easier to read and write text delimited by line breaks.
  • Both these classes ease the burden of detecting Windows- or Unix-style line breaks on input, and writing platform-specific line breaks on output.

So What Good Is Streamed Input?

Next: Streams in Memory