Use the Scanner class for reading text data from a file and the PrintWriter class for writing text data to a file. A File object encapsulates the properties of a file or a path, but it does not contain the methods for creating a file or for writing/reading data to/from a file (referred to as data input and output, or I/O for short). In order to perform I/O, you need to create objects using appropriate Java I/O classes. The objects contain the methods for reading/writing data from/to a file. There are two types of files: text and binary. Text files are essentially characters on disk. This section introduces how to read/write strings and numeric values from/to a text file using the Scanner and PrintWriter classes.
Writing Data Using PrintWriter
The java.io.PrintWriter class can be used to create a file and write data to a text file. First, you have to create a PrintWriter object for a text file as follows:
PrintWriter output = new PrintWriter(filename);
Then, you can invoke the print, println, and printf methods on the PrintWriter object to write data to a file. Figure below summarizes frequently used methods in PrintWriter.
The program below gives an example that creates an instance of PrintWriter and writes two lines to the file scores.txt. Each line consists of a first name (a string), a middle-name initial (a character), a last name (a string), and a score (an integer).
Lines 8–11 check whether the file scores.txt exists. If so, exit the program (line 10).
Invoking the constructor of PrintWriter will create a new file if the file does not exist. If the file already exists, the current content in the file will be discarded without verifying with the user.
Invoking the constructor of PrintWriter may throw an I/O exception. Java forces you to write the code to deal with this type of exception. For simplicity, we declare throws IOException in the main method header (line 6).
You have used the System.out.print, System.out.println, and System.out.printf methods to write text to the console. System.out is a standard Java object for the console output. You can create PrintWriter objects for writing text to any file using print, println, and printf (lines 17–20).
The close() method must be used to close the file (line 23). If this method is not invoked, the data may not be saved properly in the file.
Closing Resources Automatically Using try-with-resources
Programmers often forget to close the file. The followings new try-with-resources syntax automatically closes the files.
try (declare and create resources) {
Use the resource to process the file;
}
Using the try-with-resources syntax, we rewrite the code in the program above, WriteData.java, in the program below.
A resource is declared and created followed by the keyword try. Note that the resources are enclosed in the parentheses (lines 12–15). The resources must be a subtype of AutoCloseable such as a PrinterWriter that has the close() method. A resource must be declared and created in the same statement and multiple resources can be declared and created inside the parentheses. The statements in the block (lines 15–21) immediately following the resource declaration use the resource. After the block is finished, the resource’s close() method is automatically invoked to close the resource. Using try-with-resources can not only avoid errors but also make the code simpler.
Reading Data Using Scanner
A Scanner breaks its input into tokens delimited by whitespace characters. To read from the keyboard, you create a Scanner for System.in, as follows:
Scanner input = new Scanner(System.in);
To read from a file, create a Scanner for a file, as follows:
Scanner input = new Scanner(new File(filename));
Figure below summarizes frequently used methods in Scanner.
The program below gives an example that creates an instance of Scanner and reads data from the file scores.txt.
Note that new Scanner(String) creates a Scanner for a given string. To create a Scanner to read data from a file, you have to use the java.io.File class to create an instance of the File using the constructor new File(filename) (line 8), and use new Scanner(File) to create a Scanner for the file (line 11).
Invoking the constructor new Scanner(File) may throw an I/O exception, so the main method declares throws Exception in line 6.
Each iteration in the while loop reads the first name, middle initial, last name, and score from the text file (lines 14–20). The file is closed in line 23.
It is not necessary to close the input file (line 23), but it is a good practice to do so to release the resources occupied by the file. You can rewrite this program using the try-with-resources syntax.
How Does Scanner Work?
The nextByte(), nextShort(), nextInt(), nextLong(), nextFloat(), nextDouble(), and next() methods are known as token-reading methods, because they read tokens separated by delimiters. By default, the delimiters are whitespace characters. You can use the useDelimiter(String regex) method to set a new pattern for delimiters.
How does an input method work? A token-reading method first skips any delimiters (whitespace characters by default), then reads a token ending at a delimiter. The token is then automatically converted into a value of the byte, short, int, long, float, or double type for nextByte(), nextShort(), nextInt(), nextLong(), nextFloat(), and nextDouble(), respectively. For the next() method, no conversion is performed. If the token does not match the expected type, a runtime exception java.util.InputMismatchException will be thrown.
Both methods next() and nextLine() read a string. The next() method reads a string delimited by delimiters, and nextLine() reads a line ending with a line separator.
The line-separator string is defined by the system. It is \r\n on Windows and \n on UNIX. To get the line separator on a particular platform, use
String lineSeparator = System.getProperty("line.separator");
If you enter input from a keyboard, a line ends with the Enter key, which corresponds to the \n character.
The token-reading method does not read the delimiter after the token. If the nextLine() method is invoked after a token-reading method, this method reads characters that start from this delimiter and end with the line separator. The line separator is read, but it is not part of the
string returned by nextLine().
Suppose a text file named test.txt contains a line
34 567
After the following code is executed,
Scanner input = new Scanner(new File("test.txt"));
int intValue = input.nextInt();
String line = input.nextLine();
intValue contains 34 and line contains the characters ' ', 5, 6, and 7.
What happens if the input is entered from the keyboard? Suppose you enter 34, press the Enter key, then enter 567 and press the Enter key for the following code:
Scanner input = new Scanner(System.in);
int intValue = input.nextInt();
String line = input.nextLine();
You will get 34 in intValue and an empty string in line. Why? Here is the reason. The token-reading method nextInt() reads in 34 and stops at the delimiter, which in this case is a line separator (the Enter key). The nextLine() method ends after reading the line separator and returns the string read before the line separator. Since there are no characters before the line separator, line is empty.
You can read data from a file or from the keyboard using the Scanner class. You can also scan data from a string using the Scanner class. For example, the following code
Scanner input = new Scanner("13 14");
int sum = input.nextInt() + input.nextInt();
System.out.println("Sum is " + sum);
displays
The sum is 27