File Input/Output

We already know much of what we need for reading and writing files of text (characters). For input, we use the Scanner class, and for output we use the print() and println() methods.

Making a Scanner from a filename

We have previously constructed an instance of class Scanner from the existing input stream System.in. To open an existing file as a character stream, we instantiate the class FileInputStream, from which we can then create a Scanner.

Writing to a file

To write to a file, we create an instance of PrintWriter, giving it the name of the file we want to write. Warning: Doing this will overwrite the file, deleting whatever was already in it. So be careful about what you open in this way.

Finishing up

Whenever we open a file, we should be careful to close it. Both Scanner and PrintWriter provide a method close().

Exceptions

Note that the declarations of the constructors for FileInputStream and PrintWriter include a clause throws FileNotFoundException. Note that both this exception and its slightly more general supertype IOException belong to java.io; so you will have to either import these names or use a qualified name.

It is also possible that reading or writing an open file will throw an IOException, although the Scanner and PrintWriter classes will not do that. If you operate more directly on an open file, you will need to do the reading or writing inside a try with an appropriate catch clause. The reason may not be obvious to you, but the close() method can also throw an IOException.

For programs that are not trivial, it is important to remember to close any file that you open. A good way to do that is to wrap the code that processes the file in a try...finally. What this means in practice is that file processing usually looks something like this:

    ... get a filename or File ...
    Scanner f;
    try {
      f = new Scanner(new InputStream(...name or File...));
    }
    catch (IOException e) {
      ... deal with the failure ... and return or exit
    }

    try {
        ... process the file ...
    }
    finally {
        f.close();
    }

You would treat a PrintWriter in a manner similar to the way the Scanner is handled above.

For-each loops

We can interate through the elements of an ArrayList with something like this:

    for (Iterator<String> it = a.iterator(); a.hasNext(); ) { 
        String x = it.next(); 
        // do something with x 
    }

This is so common that Java provides special syntax for it:

    for (String x: a) { 
      // do something with x 
    }

This is called a for-each loop, because it executes the body of the loop for each element in a.

To be able to use a for-each, the collection must implement the Iterable interface—which most of our classes do. So the code above would work if a were a HashSet, too.

The exception is HashMap, because it does not have an iterator() method. (Can you remember why?) So a map we would need something like

    HashMap<String, Integer> m ... 
 
    for (String k: m.keySet()) { 
      int v = m.get(k); 
      // do something with k and v 
    }