CS 151 Fall 2007 Lab 11
Reading
Input, Writing Output, Handling Exceptions
The main goal for this lab is to learn more about reading
input from and writing output to files.
As we will see, this is often an error prone exercise. Consequently, file manipulation
programs also require quite a bit of error checking and handling which is primarily
done in Java by throwing and catching exceptions. Special thanks to Prof. Jen Dillion who teaches
Biostatistics for the idea to use U.N. data and for extracting the data from
the U.N.'s site for me.
- Open
BlueJ either by selecting it from the Dock or navigating to it in the Applications
folder and double clicking it.
- Mount your network
directory/folder so that you can access the lab after you leave Olin
323. Go to the Finder and
from the
Go menu select Connect to Server.... Type afp://fileserver1 or choose it from the
list of selections and select the Personal volume
when prompted.
- From
the Project menu select New Project. Select your computer from the drop
down menu. Then scroll down
and open the Volumes folder and then the Personal folder inside it. Scroll down the long list of
folders until your find yours.
Create a project called "Lab11" in your personal network
folder.
- Download the compressed
file data.zip. Copy it to your newly created Lab11 project folder in
the Finder (not in BlueJ). By
default, data.zip should be on the Desktop, so you can just drag and drop
it to your personal folder.
Once you've done that, double-click data.zip to extract the multiple
files it contains.
- Create a class called UNDataReader
and remove the default instance variable and method that BlueJ puts in the
class. Create the usual
static main method and don't forget that it takes a parameter called args whose
type is an array of Strings.
- In the very first line
of the class, add an import statement for the Scanner class from the
java.util package. Add a
second import statement for all needed classes (using the * wildcard) from
the java.io package.
- Create a variable of
type Scanner in the main method called console and assign it a Scanner
object that reads from the Terminal window. Add a printed message prompting the user for the name
of a file to read data from. Add
a line that reads the user's response and stores it in a String variable,
e.g. called fileName. Compile
your code and fix any errors.
- Next we want to create
a Scanner object that reads from the user-specified file name instead of
from the Terminal.
Unfortunately, we can't just create a Scanner using the file name. Feel free to try it and see what
happens. Instead we first
create a FileReader using the file name and then create a Scanner that
uses the FileReader. If you
think this is complicated, before this version of Java it was even worse!
- Define a variable of
type FileReader called reader. Assign to it a FileReader object constructed with the
user-provided filename as a parameter, e.g.
reader = new FileReader(filename). Try to compile after adding this
line. The class won't compile
because the constructor for FileReader may throw a FileNotFoundException
if it can't find the file. All
the exceptions (error conditions) we have encountered so far have been
unchecked exceptions. Java
doesn't require that we explicitly handle unchecked exceptions. Checked exceptions such as
FileNotFoundException, on the other hand, may arise due to circumstances
beyond our control. Java requires
our methods to explicitly manage such errors. We can either catch the error and take some action or simply
throw (pass on) the error and let some higher level method deal with the
problem. The second approach
is suitable here. So, fix the
problem by add the phrase throws
FileNotFoundException
after the declaration of/interface for main but before its body (the part
in {}). Compile again and the
compiler should now be happy.
- Create a second Scanner
variable called input and initialize it with a new Scanner using reader as
a parameter.
- Open the file
cinema_data1.txt using TextEdit or any editing/word processing
application. We want to read
in the three entries (usually called fields) from each line (usually
called a record). See the
Java Class documentation for more information on Scanner's methods. We will use nextInt(), next(), and
nextDouble to read in the data.
We will also use hasNextLine() to test when we have reached the end
of the file and there is no more data to read.
- Add the following lines
to you main method, to read and display the contents of the first record
in the file:
// this first line is just
a header
System.out.println("Country Code, Country Name, Cinema Attendance");
System.out.println(input.nextInt()
+ ", " + input.next() + ", " + input.nextDouble());
- Compile and run
it. Enter cinema_data1.txt
when prompted for the file name.
Run it again and this time enter the name of a file that doesn't
exist, e.g. hello.txt. As
expected, this causes a FileNotFoundException and brings everything to a screeching
halt. Not a very pleasant
experience for the user of your program!
- Multiply the attendance
by one million so that it displays the actual integer number of people. By default, double's larger than a
certain amount are displayed in exponential notation. Try casting to get an integer
displayed. Also remove the
country code from the displayed information. Even if you don't want to display it you still need to read
it! Try not reading it and
see what happens. Add a line
that just calls nextInt() but doesn't do anything with the integer it
returns. It reads the country
code and basically forgets it.
At the very end of the main method, add a line that calls the
close() method on the FileReader, e.g. reader.close(). It's good practice to always close
any files you opened. When
you try to compile you get an error about an unreported IOException. Change the description of your
main method to "throws IOException" instead of FileNotFoundException. Why doesn't it complain about FileNotFoundException
now (see p.434 in your book for the answer)?
- Add a while loop to
read and display all the records in the file. The Scanner method hasNextLine() allows you to peek
ahead and see if there is another record to be read, without actually
doing anything with that record.
Keep reading and displaying the contents of each record until there
are no more records. Compile
and run using cinema_data1.txt as the file.
- Now try it with
cinema_data2.txt. Why don't
things work the way they should?
You need to keep reading words of the country's name until you come
to a double. You can use the
method hasNextDouble() to check if the next thing you will read in next is
a double or not. Use a while
loop to build up the country's name word by word in a String variable.
- Once you can
successfully read cinema_data2.txt, try cinema_data_full.txt. This is the original data file as
downloaded from the United Nation's web site. Open the file to see what's causing the problem. You can read and discard a line by
calling the nextLine() method and not doing anything with the String it
returns. How would your fix
change if the number of initial descriptive lines wasn't always two?
- Try it again. Now
what's the problem? Look at
the records for Portugal and Zimbabwe to get an idea.
- Did
you finally get the whole file read and displayed? Congratulations.
- Try to write out the parts
of the file we want (just country name, attendance) to a new file called
"clean_cinema_data.txt" in addition to displaying it on the
screen. Add the following
near the beginning of your main method:
PrintWriter output = new
PrintWriter("clean_cinema_data.txt");
To print to the file use output.print() or output.println(). Print to both the screen and the
file and make sure the same thing goes both places. Don't forget to close output
at the end of your main method.
This is even more important for output writing than for input
reading. Failing to close()
may result in lost information.