Session 35: Text files

FileReader and FileWriter classes
    FileReader class
    FileWriter class
The PrintWriter class
    Constructor method
    Instance methods
    Example
The BufferedReader class
    Constructor method
    Instance methods
    Example
The StringTokenizer class
Exercise

FileReader and FileWriter classes

Earlier we saw the FileInputStream and FileOutputStream classes to work with files as a sequence of bytes. The java.io package also provides two other classes, FileReader and FileWriter, that work with files as a sequence of characters instead.

(This distinction between bytes and characters may seem a bit pedantic, given that the major operating systems of today use ASCII - and so characters and bytes are almost synonymous. But Java uses the Unicode character set (where a character is 16 bits long), and so it needs to distinguish the two concepts.)

FileReader class

The FileReader class works analogously to the FileInputStream class: The only change is that the read() method works with an array of characters, rather than an array of bytes.

FileReader(File file) throws FileNotFoundException
Creates a FileReader object for the specified file. (It throws FileNotFoundException if the specified file doesn't exist or isn't readable.)
int read(char[] ch) throws IOException
Attempts to fill the array ch with bytes from the file, returning the number of characters successfully read, or returning -1 if the end of the file has already been reached.
void close() throws IOException
Closes the file for reading, making further reads from the object invalid.

FileWriter class

Similarly, the FileWriter class works analogously to the FileOutputStream class, except that the write() methods work with arrays of characters. (Also, the FileWriter constructor method throws an IOException instead of a FileNotFoundException - it's a minor change, but it can affect the program code.)

FileWriter(File file) throws IOException
Creates a FileWriter object for the specified file. (It throws IOException if the specified file isn't writeable.)
void write(char[] ch) throws IOException
Writes the characters of ch into the file just after the location where it last wrote.
void write(char[] b, int offs, int num) throws IOException
Writes num characters from ch, beginning at index offs in the array, into the file just after the location where it last wrote.
void close() throws IOException
Closes the file, ensuring that everything is saved on the disk and making the object ineligible for use in further writes.

The PrintWriter class

Using FileWriter for saving into a file is pretty inconvenient: The FileWriter methods take arrays of characters as parameters, but generally we just want to print a value into the file. The java.io library provides class called PrintWriter that we can layer on top of a FileWriter, to work with the file the way we actually want to work.

Constructor method

The constructor method for a PrintWriter takes a FileWriter as a parameter.

PrintWriter(FileWriter writer)
Creates a PrintWriter object that uses writer whenever it wants to actually write something into a file.
A PrintWriter object will build up the character arrays for you, and it pass them along to the FileWriter that you give it in the constructor.

(Technically, the PrintWriter constructor actually takes a Writer object as a parameter, an abstract class which FileWriter extends. There are other classes that also extend the Writer class, and PrintWriter can be layered on top of any of these.)

Instance methods

The PrintWriter instance methods are as follows.

void print(char c)
Prints the single character c.
void print(int i)
Prints the integer i in decimal.
void print(double d)
Prints the floating-point value d.
void print(String s)
Prints the string s.
void print(Object obj)
Prints the string returned by obj.toString().
void close()
Closes the file.
Additionally, the PrintWriter instance methods have a println() method corresponding to each of the print() methods, which prints the data and then terminates the current line of input.

Example

This allows us to save into a file just as conveniently as we can print to the screen. As an example, the following program would create a table of numbers and their square roots.

import java.io.*;

public class SqrtTable {
    public static void main(String[] args) {
        PrintWriter file;
        try {
            if(args.length != 1) {
                System.err.println("usage: java SqrtTable filename");
                return;
            }
            file = new PrintWriter(new FileWriter(new File(args[0])));
        } catch(IOException e) {
            System.err.println("Error opening file " + args[0]
                + ": " + e.getMessage();
            return;
        }

        for(int i = 1; i < 10; i++) {
            file.print(i);
            file.print("  ");
            file.print(Math.sqrt((double) i));
        }
        file.close();
    }
}
Notice how the PrintWriter methods don't throw exceptions, so that the only the process of opening the file needs to go into the try block.

The BufferedReader class

Constructor method

Like PrintWriter is layered on top of a FileWriter, so the BufferedReader is layered on top of a FileReader.

BufferedReader(FileReader reader)
Creates a BufferedReader that uses reader any time it wants to fetch more characters.

(Just as before, this constructor method actually takes a Reader parameter - and FileReader extends Reader, just as some other classes also extend the abstract class Reader.)

Instance methods

There are just two instance methods worth knowing about in the BufferedReader class.

String readLine() throws IOException
Reads and returns the next line, or returns null if there are no more lines in the file. The line separator is not included in the returned string.

void close() throws IOException
Closes the file.

Example

The following program takes a file name from the command line, reads the file into memory, and then prints it out with the lines in reverse order.

import java.util.*;
import java.io.*;

public class Reverse {
    public static void main(String[] args) {
        BufferedReader file;
        try {
            if(args.length != 1) {
                System.err.println("usage: java Reverse filename");
                return;
            }
            file = new BufferedReader(new FileReader(new File(args[0])));
        } catch(FileNotFoundException e) {
            System.err.println("Error opening file " + args[0]
                + ": " + e.getMessage();
            return;
        }

        Vector lines = new Vector();
        try {
            while(true) {
                String line = file.readLine();
                if(line == null) break;
                lines.addElement(line);
            }
            file.close();
        } catch(IOException e) {
            System.err.println("Error reading file: " + e.getMessage());
            return;
        }

        for(int i = lines.size() - 1; i >= 0; i--) {
            System.out.println(lines.elementAt(i));
        }
    }
}

The StringTokenizer class

The final class we're going to look at isn't part of the java.io package at all - it's part of the java.util package. It's basic purpose is to break a string into tokens - basically, breaking it into words.

Breaking a string into tokens is often often handy when you're reading from a file: You often don't particularly care about the space separating the tokens, you just want to get the data within the tokens.

StringTokenizer(String data)
Creates a StringTokenizer, which can be used to fetch the tokens contained within data. Tokens are considered to be split by any combination of white-space characters (the space character, the tab character, the newline character, or the carriage return character).

StringTokenizer(String data, String delims)
Creates a StringTokenizer, which can be used to fetch the tokens contained within data. Tokens are considered to be split by any combination of characters contained in the string delims.

After creating a StringTokenizer object, you can use the following instance methods to get information about the tokens.

boolean hasMoreTokens()
Returns true if there are any tokens left in the token sequence.

String nextToken()
Deletes and returns the next token in the token sequence.

int countTokens()
Returns the number of tokens left in the token sequence.

As an example, the following program would read a line from the user and print out all the words the user typed.

import java.util.*;
import csbsju.cs160.*;

public class PrintWords {
    public static void main(String[] args) {
        IO.print("Line: ");
        String line = IO.readLine();
        StringTokenizer tokens = new StringTokenizer(line);
        while(tokens.hasMoreTokens()) {
            IO.println(tokens.nextToken());
        }
    }
}

Exercise