Section B.3 Files
Up to this point, we’ve entered all the data for a program as data structures in our example programs or have prompted the user to enter data from the keyboard, using a
Scanner
with System.in
. What if someone sends you a file of several months’ worth of weather data from Munich, Germany, in a file named klima.txt. (You can find this file in the code repository.)MESS_DATUM;TMK;TXK;TNK 20200106;0.8;6.0;-2.5 20200107;2.4;4.6;-2.1 20200108;3.6;7.2;-1.3 20200109;7.7;14.5;3.6 ... 20210704;17.9;22.9;14.7 20210705;18.5;22.4;13.9 20210706;21.8;31.5;15.4 20210707;18.2;20.5;16.7 20210708;16.9;20.1;14.7
The columns stand for date, average daily temperature, high temperature, and low temperature (temperatures are in °C).
If you want to find the maximum temperature and minimum temperature across the whole time period, you certainly don’t want to have to type all the numbers again at the keyboard. Instead, you want Java to be able to read the file from your disk.
Subsection B.3.1 The File
Object
In order to access a file, you must use its path name to create a
File
object. A path name describes how to get to a file in the file system. For this chapter, we’ll presume that your data files are in the same directory as the .class file for your program. That way, the path name is the same as the file name.The resulting object doesn’t give you access to the file contents; rather, it gives you access to information about the file, also known as the file’s metadata.
Here’s the start of a program that lets you enter a path name and find out about that file:
The variable
f
is what, in other programming languages, is called a file handle or file descriptor. The code continues by calling some of the more useful methods in the File
class:A couple of notes: the
length
method returns the file size in bytes. It is possible for a file to be neither a directory nor a “normal file”—the /dev/zero
path on Linux refers to a virtual file that is neither.The .
File
class also has methods that let you delete files, rename them, and create directories. For details, see the Java API documentation2
docs.oracle.com/en/java/javase/16/docs/api/java.base/java/io/File.html
Subsection B.3.2 Reading Files
Let’s write a program that opens the klima.txt file and finds the maximum and minimum daily temperature across the time period described in the file.
In order to read the contents of a file, you must open a
Scanner
based on the File
object. But if you try code like this:The compiler will complain (message reformatted to fit page width):
Klima.java:9: error: unreported exception FileNotFoundException; must be caught or declared to be thrown Scanner input = new Scanner(f); ^ 1 error
The
FileNotFoundException
is a checked exception, and the compiler insists that you either catch it or throw it to the caller.This will require you to import
java.io.FileNotFoundException
and set up a try
-catch
block.Now we are set to read the file’s contents. Rather than write the whole program right now, let’s start by reading the file one line at a time, printing them out to the screen, and counting the number of lines. Here’s the code that goes inside the
try
block. The hasNextLine
method returns false
when it hits the end of the file:Now that we know that we can sucessfully open and read the file, we can modify the code to accomplish the task we want to do: finding the minimum and maximum temperatures.
The program will read lines one at a time and then extract the data from each line. Because
Scanner
’s next
and nextDouble
methods use whitespace to separate items, we can’t use them “as-is” here, where data items are separated by semicolons. We could solve this problem by using the useDelimiter
method to change the separator to a semicolon, but that would deprive us of the opportunity to learn about a new String
method and practice more with exceptions.Let’s replace the line-counting code with this code for finding the maximum and minimum temperatures:
The new
String
method is split
. Given a delimiter, this method splits the given String
into an array of strings wherever it finds the delimiter that you give it as an argument. For example, after this code executes:The
parts
array will be {"sister", "in", "law"}
.Similarly, the program uses
oneLine.split(";")
to separate the items on each line. If there aren’t four items on a line, it’s incomplete, and the program does nothing (effectively skipping over the line).We now have a problem: the first line doesn’t have any numbers on it, and trying to use
parseDouble
on the titles will throw a NumberFormatException
. One way to solve the problem is to read in the first line before entering the while
loop and discarding the result. Another way to solve this problem (which could also occur if the file we were given had bad data in it), is to use another try
-catch
:Subsection B.3.3 Writing Files
There are many Java classes for reading files. In this book, we’ve been using
Scanner
to read input because it contains many methods to make getting input simple.In a similar way, there are many Java classes for writing files. We’ll discuss only one of them:
PrintWriter
. To use this class, you must import java.io.PrintWriter
Just as you created a
Scanner
by using a File
object as a parameter to the constructor, you can write to a disk file by creating a File
object with the path you want and then use that object as a parameter to the PrintWriter
constructor. And, just as the compiler required you to enclose the code in a try
-catch
block, you must do the same when opening a PrintWriter
:Just like
System.out
, the PrintWriter
object output
we have created has print
, println
, and printf
methods. Instead of writing to your screen, they write data to the file you specified.There are two important things to know about writing files:
-
When you open a
PrintWriter
to aFile
that does not exist, it will be created for you. If you open aPrintWriter
to aFile
that already exists, the existing file will be deleted and re-created. Any information that was in the file will be gone, even if you never write anything to thePrintWriter
.This means that it is always useful to use theexists
method to check if a file already exists and, when possible, give the user the option to overwrite the old file or exit the program. -
When you do a
println
to aPrintWriter
, the data is not written to disk immediately. Instead, it is kept in a buffer, and is written only when the buffer is full. If you exit the program with the buffer partially filled, there is a chance that it might not be written to disk. Always call theclose
method on your output files to make sure that the buffer is written to disk. If you run this program:without closing the file, the resulting write_test.txt file will be empty. If you uncomment theoutput.close();
line and run the program again, the file will contain the output.
Subsection B.3.4 try
with Resources
Because it is important to close files, Java has a syntax for associating
File
s with input and output classes as part of the try
syntax. When using this syntax, the Java Virtual Machine will automatically close the input and output when the try
block exits. Listing B.3.11 uses try
with resources to open a PrintWriter
:The declaration of
outWriter
is now in parentheses after try
rather than after the block’s opening brace. We no longer need to call outWriter.close()
—the JVM will automatically do the call when it exits the try
-catch
block.You can declare as many input and output objects as you want inside the parentheses:
The
File
declarations cannot go inside the parentheses; the compiler won’t let you do that.You have attempted of activities on this page.