Computer Science 252
Problem Solving with Java

Spring 2016, The College of Saint Rose

Lab 2: Java Review: Part 2
Due: 11:59 PM, Thursday, February 4, 2016

This is the second of the three "review" labs that are designed to refresh and sharpen your prerequisite Java programming skills. Again, there are a few programs to write, but all are things you should be comfortable doing, maybe with a little refreshing of your memory, having completed the prerequisites successfully.

You are to work individually on these tasks. Searching for similar programs on the Internet (or anywhere) is not permitted, and would be counterproductive to your ability to learn how to approach these straightforward tasks. However, you should feel free to ask your instructor, our graduate assistant, or the ASC tutors for help as you work through them.

This second review lab focuses on file input/output and exception handling. All programs for this lab are once again Java applications and all have been designated as practice programs.

Getting Set Up

To get your BlueJ environment set up for this week's lab assignment, start BlueJ and choose "New Project" from the "Project" menu. Navigate to your folder for this course and choose the name "Lab2" (no spaces) for the project.

Practice Programs: File Input and Exception Handling

Once you're comfortable with console I/O, using files for input and output in Java is just a matter of remembering to do a few more steps. There are multiple ways of doing this in Java, and you are welcome to use any. But this lab will focus on the use of the Scanner for input and the PrintWriter for output.

Suppose you have a file called "myfile.txt" in your folder with your Java program. (In BlueJ, this is the folder that has the .java, .class, and a few other files.) You can construct a Scanner to read the contents of the file and remember the Scanner in a local variable with the following line of code:

  Scanner infile = new Scanner(new File("myfile.txt"));

Note that you will also need to add an import statement:

import java.io.File;

We'll get into the things you can do with the Scanner below, but for now, we also should put in code that closes the Scanner:

  infile.close();

Any code that reads from infile goes in between the above statements.

Exception Handling

You will also need to deal with the fact that this is an operation that might go wrong. The file you're trying to read might not exist. Or you might not have sufficient privileges to read it. In that case, Java's exception handling mechanism comes into play. We can choose to ignore (almost, anyway) the exceptions in which case your program would just crash with a messy traceback, or we can "catch" them and handle them. If you ignore them, the simplest thing to do is to add a qualifier to the end of your main method's signature:

  public static void main(String args[]) throws IOException

with the corresponding import:

import java.io.IOException;

This has the advantage that once you've done it, you can use all of the File I/O classes and methods in your main method without worrying about exceptions. If one occurs, your main method will "throw" it back to the Java run-time system, which will then kill your program and print the stack trace. Disadvantages of this approach include (i) only your main method, or others with the "throws" qualifier, will be able to use I/O classes and methods, (ii) your program will crash with an ugly message if any exceptions are thrown.

If we have our programs "catch" and handle the exceptions, we can control how those error situations affect our program.

The code would look something like this, called a try-catch block:

  try {
    // do file I/O things
  }
  catch (IOException e) {
    // do something about the error described 
    // in the Exception called e
  }

In this case, we could go back and retry (maybe after prompting for a new file name) or print a message and quit anyway. Something like:

  try {
    Scanner infile = new Scanner(new File("myfile.txt"));

    // other things with "infile"

    infile.close();
  }
  catch (IOException e) {
    System.err.println(e);
    System.exit(1);
  }

This will print a human-readable message that explains the exception and then exits.

Practice Program: Write a program OpenFiles.java that reads file names from a keyboard Scanner, and attempts to open each of those files for reading. For each file name, construct and then close a file Scanner with that file name. If the construction is successful, print a message. If it is not successful, the catch block will be executed, and you should print an appropriate message there. (10 points)

Reading from the Scanner

Of course, in real programs, you are usually interested in the contents of the file. Fortunately, you use the same methods as you'd use with a keyboard Scanner. You can read the next instance of a particular data type with the next, nextLine, nextInt, etc.., methods, and can check for their existence with the hasNext, hasNextLine, hasNextInt, etc.., methods. For example, to read all integer numbers from a file "numbers.txt" and add them up, this code fragment could be used (exception handling omitted):

  Scanner numbers = new Scanner(new File("numbers.txt"));
  int sum = 0;
  while (numbers.hasNextInt()) {
    int n = numbers.nextInt();
    sum += n;
  }
  numbers.close();
  System.out.println("Sum is " + sum);

Important side notes about plain text files! Hopefully if you've gotten this far, you understand the difference between a plain text file like your Java program text, and formatted files like Word documents or PDFs. Here, we are dealing strictly with plain text files. For better or for worse, modern operating systems (Windows, Mac OS X) try their best to stop users from dealing with plain text. Many programs store files in more complicated formats unnecessarily, and the systems often hide file extensions from you. Please don't hesitate to ask if you have trouble creating and editing plain text files.

Practice Program: Write a program ReadAllDoubles.java that reads an entire text file, and adds up any numbers encountered on the input, then prints the sum at the end. The name of the file should be read from a keyboard Scanner. Big hint: you should continue reading the file as long as any input remains (that is, hasNext returns true), and for each item, if it can be read as a double value, read it as one (nextDouble) and add it to your sum. If it can't, read it as a String (next) and ignore it. (10 points)

Note that your program should only count numbers that are readable with Scanner's nextDouble method. So a number embedded within a word or with punctuation at the end will not be counted. Consider this sample input file:

4 Little Pr1mes: 2 3 5 7!
But are 2.5 and 10.75 prime? -1?

My program prints:

Sum is 27.25

which is the sum of the 4, 2, 3, 5, 2.5, and 10.75. The embedded "1" and the ones at the end of the lines with punctuation at the end are not readable as double values by the Scanner, so are ignored as non-numeric text.

Practice Program: File Output

File output is also analogous to console I/O. Rather than calling methods of System.out (most commonly print and println), we construct and call methods on a PrintWriter object.

Java API Documentation: java.io.PrintWriter

This code segment will create a plain text file with the first line of a famous book (exception handling omitted):

  PrintWriter outfile = new PrintWriter(new File("ss.txt"));
  outfile.println("Mr. and Mrs. Dursley of Number Four, Privet Drive,");
  outfile.println("were proud to say that they were perfectly normal,");
  outfile.println("thank you very much.");
  outfile.close();

As you can see, once a PrintWriter is constructed and a reference to it is remembered in a variable, that variable can be used like "System.out" is used for console output. Really, that's all there is to it!

Practice Program: Write a program Odds.java, that prints the first n odd numbers to a file odds.txt. n should be read from a keyboard Scanner. Note: this is not asking for the odd numbers up to n, it's asking for the first n odd numbers. (5 points)

Practice Program: Combining Input and Output

Practice Program: Write a program TravelLog.java that reads in two file names from a keyboard Scanner. It then reads the contents of the first file (a "travel log file"), which is a list of road names followed by a series of numbers that represent distances, in miles, of trips on that road over some time period. For each road, an entry is written to the output file (a "travel summary file") with the road name followed by the total distance traveled on that road during the given period. A grand total distance traveled is written on the last line of the travel summary file. Further details below. (15 points)

For my version of the program, if the input file contains the text:

I-90
7
3.75
30
8.7
I-87
4
104.3
US20
0.5
87.2
5.4
29.3

then the output file would contain:

I-90: 49.45
I-87: 108.3
US20: 122.4
Grand total: 280.15

Note that formatting is not important in the input file. That is, all of the names and numbers could be broken into any number of lines. For example, I-90's entry could be all on a single line:

I-90 7 3.75 30 8.7

Fortunately, if you use the Scanner's methods correctly, the program does not need to know or care exactly how the input file is formatted.

Note: you can and should assume that road names begin with a letter and contain no spaces. Hint: this means you can read them in their entirety with the Scanner's next method.

Submitting

Before 11:59 PM, Thursday, February 4, 2016, submit your lab for grading. To complete the submission, email a copy of your lab (a .7z or .zip file containing your project directory) to terescoj AT strose.edu.

Grading

This assignment is worth 35 points, which are distributed as follows:

> FeatureValueScore
OpenFiles correctness 5
ReadAllDoubles correctness 10
Odds correctness 5
TravelLog correctness 15
Total 35