6. Reading a Text File

Part of CS:2820 Object Oriented Software Development Notes, Fall 2015
by Douglas W. Jones
THE UNIVERSITY OF IOWA Department of Computer Science

 

Where were we?

Putting everything together from the previous lecture, we have the following class definitions for building our road network:

import java.util.LinkedList;

/** Roads are one-way streets linking intersections
 *  @see Intersection
 */
class Road {
        float travelTime;         //measured in seconds
        Intersection destination; //where the road goes
        Intersection source;      //where the comes from
        // name of road is source-destination
}

/** Intersections join roads
 *  @see Road
 */
class Intersection {
        String name;
        LinkedList <Road> outgoing = new LinkedList <Road> ();
        LinkedList <Road> incoming = new LinkedList <Road> ();
        // Bug: multiple types of intersections (uncontrolled, stoplight)
}

Aside: It is extremely useful to adopt a standard way of noting known bugs in a program. Here, we adopt the convetion that bugs are documented by comments that begin // Bug: followed by a description of the problem. If you adopt such a convention, you can use text searches to find known bugs, and when it is not obvious what to do next on a project, you can make progress by searching for a bug you know how to solve, at least partially, perhaps leaving more bug notices in the code.

It's time to start working on initializing a road network. In theory we could build the road-network description in many ways. For example, we could build a GUI, for example, where users could point and click to drag and drop intersections and roads into position. Or, we could process a text file containing a description of the road network. The latter alternative is easier. Consider, for example, a file that was structured as follows:

intersection ...
intersection ...
intersection ...
road ...
road ...
road ...
road ...
road ...

Each line of the file begins with a keyword, either road or intersection, followed by attributes of that road or intersection. We will expand on this description of the text file after we make some progress toward reading it.

Access to the text file

Java provides a very useful class for reading text files, the scanner. To quote the official definition: "A Scanner breaks its input into tokens using a delimiter pattern, which by default matches whitespace. The resulting tokens may then be converted into values of different types using the various next methods." The setup for calling a scanner is as follows:

import java.io.File;
import java.util.Scanner;

/** RoadNetwork, the main class to build a network of roads and intersections.
 *  @see Road
 *  @see Intersection
 */
public class RoadNetwork {
        public static void main(String[] args) {
                // Bug:  Ought to check to see if there is a file name
                Scanner sc = new Scanner(new File(args[0]));
                // Bug:  What if the file doesn't exist?
        }
}

Aside: The main class of the program must be declared as public class and the class name must match the file name. The main method must be declared as a public static void method with the name main, and it must take an un-dimensioned array of strings as an argument. By convention, this parameter is called args because it holds the arguments that were used to launch the program. This mix of required text and traditional text is called boilerplate. The concept of boilerplate text comes from legal documents, where boilerplate text is text that has been tested in court for generations and is therefore known to be resistant to challenge (like armor plate or the plate steel used to make steam boilers). Lawyers copy boilerplate from lawbooks instead of writing creatively in order to avoid the risk that they might overlook some detail that creates text that is vulnerable in court. Any text that you copy from a standard form instead of writing creatively has come to be knwn as boilerplate.

The above code creates a scanner called sc that reads from a file whose name is give by the first command line argument passed when launching the main program.

Consider running your program with this command:

[HawkID@serv15 ~/project]$ java RoadNetwork IowaCity.txt

This launches RoadNetwork.class, the file created by compiling RoadNetwork.java. Inside the main method of this class, you will find that args[0] has been set to the value "IowaCity.txt", so the call to the initializer

                Scanner sc = new Scanner(new File(args[0]));

is equivalent to

                Scanner sc = new Scanner(new File("IowaCity.txt"));

There are a number of different initializers for class Scanner, but the one we are calling here expects an open file as a parameter, and the initializer we are using for class File takes the file name, as a string, as a parameter.

Of course, the skeletal definition given above has some bugs. The code ought to check that there is a command line argument before using it, and it ought to output a sensible error message if the file does not exist or cannot be read. We need to fix the latter to make this file compile, and we will do this in the next class.