Computing Concepts with Java Essentials
Laboratory Notebook
Chapter 10 - Files

Cay S. Horstmann
Geof Pawlicki


Your name:
Your email address:
Your student ID number:

Once this form has been customized for your institution, you can use this button to send your lab work. Be sure to read the instructions before starting your work.


Lab Objectives

To gain experience with


P1. Reading Text Files

You already know how to read data from a file and write data to a file, by using the system variables Console.in and System.out together with command-line redirection (java myprog <input.txt >output.txt).

However, this approach is limited. You need to learn to use files directly in your program in the following situations:

In these situations, you need files. Java supports many file types. In this lab, you will learn about three of them: the TextInputStream and TextOutputStream classes that are part of the ccj package, and the RandomAccessFile class that is part of the standard java.io package. (It turns out that text input and output is currently somewhat limited, difficult to use and inconsistent among the various Java versions. For that reason, you will use the ccj text stream classes in this lab. The operations of the standard Java stream classes are very similar, and the techniques that you use in this lab will help you using those stream classes as well.)

To open a file, first define an object of type FileInputStream and then pass the file name to the constructor.

TextInputStream myData = new TextInputStream("input.txt");

Next, you should check that the file was opened successfully:

if (myData.fail())
   System.out.println("Unable to open input.txt");

Then you can read with the same commands that you used with Console.in, such as readInt, readDouble, readLineetc.

After reading an input, always call the fail method. When it returns true, you have reached the end of input and you must ignore the return value of the last read command.

To write output to a file, you make a TextOutputStream object and you write to it with the same print and println methods that you always used with System.out.

Write a program which compares the contents of two files. Have your program display the line number and text of the first pair of lines that differ.

First, prompt the user for the names of the two files. Then open each file. Remember to check for failure. Keep reading a line from each of them. Increment a line number counter. If both inputs fail, print a message indicating that both files are identical. If one of the inputs fails and the other does not, print a message indicating which file is shorter, then exit. If the two input lines are different, print the line number counter and both input lines, then exit. If the two input lines are identical, keep on reading.


P2. Reading Input Data in a Graphics Program

Consider a graphics program that prompts the user to define shapes by selecting menu options and clicking on a window. When the user is done, the user's work should be stored in a file so that the user can retrieve it later. Graphics can be stored in a variety of formats. Choosing the best format for a particular purpose involves factors such as speed, portability to different platforms and programs, and the storage space and transmission requirements. In this lab exercise, we will use a simple text file containing lines of the form:

Circle 1 3 5.0
Circle -4 3 5.0

That is, a circle is specified by the x- and y-coordinates of it's center, followed by it's radius.

When the program starts, all saved data are read in automatically from the file saved.dat. When the user adds more circles, then the new circles are appended to that file.

/**
 * Read shapes from a file and display them
 * @param shapeFile the file containing the shape descriptions
 * @return a vector that holds all circles from the file
 */
public static Vector readShapes(TextInputStream shapeFile)
{  /* your work here */
}

/**
 * Add a circle shape to a file
 * @param shapeFile the file containing the shape descriptions
 * @param c the circle to add
 */
public static void addCircle(TextOutputStream shapeFile, Circle c)
{  /* your work here */  
}

public static void main(String[] args)
{  TextInputStream inshapes = new TextInputStream("saved.dat");
   Vector c = readShapes(inshapes);
   /* your work here--close inshapes, then open outshapes to write to saved.dat */

   /* your work here--display all circles in the vector and write them to outshapes */
   while (true)
   {  if (readString("More circles? (Y/N)")).toUpperCase().equals("N")) 
      {  outshapes.close();
      }
      Point center = cwin.readMouse("Center: ");
      double radius = cwin.readDouble("Radius: ");
	     Circle c = new Circle(center, radius);
      c.draw();
      addCircle(outshapes, c);
   }
}

Note: The next time the user starts, the newly added circles are automatically displayed.

Now implement the circle drawing program. Extra credit if you enhance it to handle both circles and lines!


P3. Command Line Arguments

Extend your file comparison program in two ways. First, make it possible to specify the file names on the command line. Only ask the user for the file names when they are not specified. And support a command line flag to control the number of differences identified. That is:

differ -a file1.txt file2.txt       
   displays all differences
differ -n  file1.txt file2.txt
   displays the first n differences
   For example
differ -20   
   displays the first 20 differences and prompts the user for the file names

P4. Random Access

If a file stores records of fixed length, it is easy to locate any one of them. If the records are also in a sorted order, for example employee records sorted by name, it is also possible to implement a fast search strategy called binary search. Rather than having to step through every record, you can eliminate half of the records remaining at each stage of the search because they are either too big or too small.

Consider searching for the name "Mushroom" in the following listing.

  Acorn, John         34000
  Barley, Lawrence    37500 
  Corn, Charlie       29000
  Flax, Winston       43700
  Grape, Priscilla    35800
  Kiwi, Rachel        28700
->Lime, Lucy          45400
  Melon, Walker       33000
  Nasturtium, Ken     57000
  Olive, Ollie        29500
  Pickle, Jim         32000
  Radish, Ruby        32500
  Squash, Slim        44700
  Turnip, Misha       27500

First, note that there are 14 records. Start in the middle, at record 7, "Lime". Compare it with the search term, "Mushroom". Since "Mushroom" > "Lime", there is no need to look for it in the lines containing "Acorn" through "Lime".

  Melon, Walker       33000
  Nasturtium, Ken     57000
  Olive, Ollie        29500
->Pickle, Jim         32000
  Radish, Ruby        32500
  Squash, Slim        44700
  Turnip, Misha       27500

Go to the middle again. This time the middle among records 8 through 14 is record 11,"Pickle". Similarly, since "Mushroom" < "Pickle", "Pickle" through "Turnip" can be eliminated from further consideration.

That leaves only records 8 through 10. The middle record is 9.

  Melon, Walker       33000
->Nasturtium, Ken     57000
  Olive, Ollie        29500

That record is larger than "Mushroom", so we are left with records 8 through 8, i.e. a single record.

->  Melon, Walker       33000

Since this is not a match, we see that the name Mushroom is not in the database!

Here is the pseudocode to locate the value e, using binary search.

from = 0; 
to = index of last record;  

while (from <= to)
{  int mid = (from + to) / 2;
   if (value at mid equals e) return mid;
   else if (value at mid comes before e)
      from = mid + 1;
   else
      to = mid - 1;
}
/* e not found */

Enhance Database.java from the textbook to prompt for a user input lastname, then search for it using a binary search strategy. (Of course, you must use a data file that is sorted).


Don't forget to send your answers when you're finished.