Now that we know how to reach a data source or sink, such as a file, we can think about reading from it or writing to it.

Readers

Reading is done via the class, Reader, an abstract class that is made concrete by one of several subclasses, depending on the data source and the form in which you want to receive the data (int, byte, char, String). For data from files returned as char, this is FileReader, which is a subclass of InputStreamReader, which is in turn a subclass of Reader. The fact that InputStreamReader exists attests to the fact that all input is treated as a continuous stream of characters.

So here’s how you can use FileReader to read character input:

// 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);

// Create an array of char to accept the data.
char[] buffer = new char[1024];

// Establish a variable to receive the number of characters
// read.
int charsRead;

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

Writers

Not surprisingly, each of the Reader classes above has a corresponding Writer: FileWriter is a subclass of OutputStreamWriter is a subclass of Writer. And just as InputStreamReader is an acknowledgement that input is a continuous stream of characters, so OutputStreamWriter indicates that output is too.

Incidentally, the constructor for FileWriter has an overload that allows you to append to the end of an existing file.

Let’s add code to copy our input file to an 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);

// 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);

// Create an array of char to accept the data.
char[] buffer = new char[1024];

// Establish a variable to receive the number of characters
// read.
int charsRead;

// We'll read from the file until we run out of data, as
// signified by charsRead being zero. Note the syntax
// that accomplishes reading into the buffer AND testing
// the result.
try {
    while ((charsRead = rdr.read(buffer)) > 0) {
       // Write the number of characters read, starting with 
       // position 0 of the buffer.
       wtr.write(buffer, 0, charsRead);

       // Flush the output to the file. If this isn't 
       // done occasionally, output can be lost
       // if the program is interrupted.
       wtr.flush();

} finally {
    rdr.close();
    wtr.close();
}

Finally, suppose that as we’re reading the data, we’d like to copy it to a String and write it to the console. For this we’ll need to use a method ensures we don’t print too many characters, and we’ll throw in a String constructor just for fun.

// 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);

// 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);

// Create an array of char to accept the data.
char[] buffer = new char[1024];

// Establish a variable to receive the number of characters
// read.
int charsRead;

// We'll read from the file until we run out of data, as
// signified by charsRead being zero. Note the syntax
// that accomplishes reading into the buffer AND testing
// the result.
try {
    while ((charsRead = rdr.read(buffer)) > 0) {
       // Display the input on the console.
       char[] displayArray = Arrays.copyOf(buffer, charsRead);

       // Notice that we're using print() and not println().
       // If the input already has newline characters, 
       // a line break will occur when they're printed.
       System.out.print(displayArray);

       // There's also a String constructor that will
       // convert the char array to a String.
       String throwaway = new String(displayArray);

       // Write the number of characters read, starting with 
       // position 0 of the buffer.
       wtr.write(buffer, 0, charsRead);

       // Flush the output to the file. If this isn't
       // done occasionally, output can be lost
       // if the program is interrupted.
       wtr.flush();

} finally {
    rdr.close();
    wtr.close();
}

Exercise

For this exercise, write a Java project to copy an input file to an output file as described above, using FileReader and FileWriter. You can create the input file, and aim the output file, anywhere you’d like.

If you’re stuck, 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.

What You Need to Know

  • Readers and Writers are classes that perform the work of reading from and writing to data sources.
  • Input and output is done by reading and writing continuous streams of characters.
  • FileReader and FileWriter read and write streams of char from and to files.

Next: Text file input and output