Section4.6From the Java Library: java.io.File and File Input (Optional)
In addition to command-line and GUI user interfaces, there is one more standard user interface, files. In this section we show how the Scanner class, that was used in Chapter 2 for keyboard input, can also read input from files. Reading input from a file is relevant to only certain types of programming problems. It is hard to imagine how a file would be used in playing the One Row Nim game but a file might very well be useful to store a collection of riddles that could be read and displayed by a Java program. We will develop such a program later in this section.
Java has two types of files, text files and binary files. A text file stores a sequence of characters and is the type of file created by standard text editors like NotePad and WordPad on a Windows computer or SimpleText on a Macintosh. A binary file has a more general format that can store numbers and other data the way they are stored in the computer. In this section we will consider only text files. Binary files are considered in Chapter 11.
Subsection4.6.1File Input with the File and ScannerClasses
An instance of the java.io.File class stores information that a Scanner object needs to create an input stream that is connected to the sequence of characters in a text file. A partial list of the public methods of the File class is given in the UML class diagram in Figure 4.6.1. We will need to use only the File() constructor in this section.
The File instance created with the statement below will obtain and store information about the "riddles.txt" file in the same directory as the java code being executed, if such a file exists. If no such file exists, the File object stores information needed to create such a file but does not create it. In Chapter 11, we will describe how other objects can use a file object to create a file in which to write data. If we wish to create a File object that describes a file in a directory other than the one containing the Java program, we must call the constructor with a string argument that specifies the file’s complete path name—that is, one that lists the sequence of directories containing the file. In any case, while we will not use it at this time, the exists() method of a File instance can be used to determine whether or not a file has been found with the specified name.
File theFile = new File("riddles.txt");
In order to read data from a file with a Scanner object we will need to use methods that were not discussed in Chapter 2. An expanded list of methods of the Scanner class is given in Figure 4.6.2. Note the there is a Scanner() constructor with a File object as an argument. Unlike the other create() method that was used in Chapter 2, this create() throws an exception that must be handled. The following code will create a Scanner object that will be connected to an input stream that can read from a file:
We will discuss the try-catch commands when exceptions are covered in Chapter 10. Until then, the try-catch structures can be copied exactly as above, if you wish to use a Scanner object for file input. In the code above, the useDelimiter() method has been used to set the Scanner object so that spaces can occur in strings that are read by the Scanner object. For the definition of a class to read riddles from a file, the above code belongs in a constructor method.
After we create a Scanner object connected to a file, we can make a call to nextInt(), nextDouble(), or next() method to read, respectively, an integer, real number, or string from the file. Unlike the strategy for using a Scanner object to get keyboard input, it is suggested that you test to see if there is more data in a file before reading it. This can be done with the hasNext(), hasNextInt(), and hasNextDouble() methods. These methods return the value true if there are more data in the file.
The program in Figure 4.6.3 is the complete listing of a class that reads riddles from a file and displays them.
Note that, in the body of the method readRiddles(), the statements below make explicit the fact that variables that refer to objects are assigned null as a value when they are declared.
The statements below will read String s into the variables ques and ans only if the file contains lines of data for them. Otherwise the readRiddle() method will return a null value. The main() method uses this fact to terminate a while loop when it runs out of string data to assign to Riddle questions and answers.
if (fileScan.hasNext())
ques = fileScan.next();
if (fileScan.hasNext())
{ ans = fileScan.next();
theRiddle = new Riddle(ques, ans);
}
There is a separate method, displayRiddle() using a separate instance of Scanner attached to the keyboard to display the question of a riddle before the answer.
The contents of the "riddles.txt" file should be a list of riddles with each question and answer on a separate line. For example, the following three riddles saved in a text file would form a good example to test the RiddleFileReader class.
What is black and white and red all over?
An embarrassed zebra
What is black and white and read all over?
A newspaper
What other word can be made with the letters of ALGORITHM?
LOGARITHM
When the main() method is executed, the user will see output in the console window that looks like:
What is black and white and red all over?
Input any letter to see answer: X
An embarrassed zebra
What is black and white and read all over?
Input any letter to see answer:
Files are covered in depth in Chapter 11. Information on writing data to a file and reading data from a file without using the Scanner class can be found in that chapter.
ExercisesSelf-Study Exercises
1.NumberFileReader.
Using a development environment that allows user input, modify the RiddleFileReader class to create a program NumberFileReader that opens a file named "numbers.txt" and reports the sum of the squares of the integers in the file. Assume that the file "numbers.txt" contains a list of integers in which each integer is on a separate line. The program should print the sum of the squares in the System.out console window. In this case, there is no need to have a method to display the data being read or a Scanner object connected to the keyboard. You will want a constructor method and a method that reads the numbers and computes the sum of squares.