Final Topics

To complete the subjects we need to consider:

Nested Loops

When code is repetitive, the use of nested loops can allow a much more compact representation.

Example.  Write a program that creates 9 rows and 9 columns of asterisks (*).

//File:    FunAndGames0.java
public class FunAndGames0
{
    public static void main (String [] args)
    {
        System.out.println ("**********");
        System.out.println ("**********");
        System.out.println ("**********");
        System.out.println ("**********");
        System.out.println ("**********");
        System.out.println ("**********");
        System.out.println ("**********");
        System.out.println ("**********");
        System.out.println ("**********");
        System.out.println ("**********");
    }
}

The following outputs results.

**********
**********
**********
**********
**********
**********
**********
**********
**********
**********

Although the program works as desired, it is, I think you would agree, rather trite.  A somewhat more interesting approach would be to utilize a nested loop as follows.

//File:    FunAndGames1.java
public class FunAndGames1
{
    public static void main (String [] args)
    {
        for (int row=0; row < 10; row++)    //<----
        {
            //Print a line of *'s
            for (int col = 0; col < 10; col++)
            {
                System.out.print ("*");
            }
            System.out.println(); //go to next line
        }
    }
}

Suppose we want to create a triangle of *'s as illustrated below

*
**
***
****
*****
******
*******
********
*********
**********

How about

//File:    Triangle.java
public class Triangle
{
    public static void main (String [] args)
    {
        for (int row=0; row < 10; row++)
        {
            //Print a line of *'s
            for (int col = 0; col < row+1; col++)    //<-----
            {
                System.out.print ("*");
            }
            System.out.println(); //go to next line
        }
    }
}

Finally, we attempted to create the following:

******
*****
****
***
**
*
**
***
****
*****
******

After some trial and error, you'll find the following does the trick:

//File:    Vee.java
public class Vee
{
    public static void main (String [] args)
    {
        for (int row=0; row < 11; row++)
        {
            //Print a line of *'s
            for (int col = 0; col <= Math.abs (row-5); col++)
            {
                System.out.print ("*");
            }
            System.out.println(); //go to next line
        }
    }
}


Lab Exercise 1.  (p. 279/#2)  Write an application that prints a "sawtooth" using asterisks:

*
**
***
****
*****
*
**
***
****
*****
*
**
***
****
*****

The program should read from the Keyboard two integers, telling (1) how wide to make each triangle and (2) how many triangles to print.  Give it a try.   If you need inspiration, feel free to check Prof. Steinmetz's solution this one (Teeth.java).


Lab Exercise 2.  (p 280/#5)  To assist a yong friend learning to multiply, write an application to print the following multiplication table:

                    MULTIPLICATION TABLE

1     2     3     4      5     6     7      8     9     10      11     12
2     4     6     8      10   12   14  16   18     20     22     24
3     6    
4     8    
5     10   
6     12    
7     14                                  etc
8     16    
9     18   
10    20    
11    22   
12    24

More on Arrays

We have made some use of arrays in the past. An array can consist of a list in a single direction, or can be two dimensional (a rectangular array) or can have even more dimensions. Multidimensional arrays are homogenous structures; that is, they store data of only one type.  No mixing and matching.  For example, it's possible to represent a table (2-dim array) of int's or a table of char's, but not a table with a mixture of int's and char's.   This is actually a feature of all arrays, one-dimensional as well as multidimensional.

Example.  Write a program that inputs and outputs a gradebook.   In particular, the user is asked for the number of students and quizzes respectively.  The scores are read in one by one and then a table with headings is output to the screen.

//File:    TestTable0.java
import iostuff.*;
public class TestTable0
{
    public static void main (String [] args)
    {
        int [] [] score = new int [30] [15];
       
        System.out.print ("How many students are enrolled? ");
        int students = Keyboard.readInt();
       
        System.out.print ("How many quizzes were given? ");
        int quizzes = Keyboard.readInt();
       
        for (int student = 0; student < students; student++)
        {
            System.out.println ("Enter scores for student " + (student+1));
            for (int quiz=0; quiz < quizzes; quiz++)
            {
                score [student] [quiz] = Keyboard.readInt();
            }
        }       
       
        //The following sets up the headers for the table
        System.out.println ("The gradebook is as follows: ");
        System.out.print ("Quiz\t");
        for (int k=1; k<=quizzes; k++)
        {
            System.out.print (k + "\t");
        }
        System.out.println();
        System.out.println();
       
//The following displays the table of scores in the gradebook
        for (int s=0; s<students; s++)
        {
            System.out.print (s+1);
            for (int q=0; q<quizzes; q++)
            {
                System.out.print ("\t" + score [s] [q]);
            }
            System.out.println();
        }
    }
}

You may remember that we collected several useful list processing methods into the NumberList class.  We could do the same here.  In particular, suppose we abstract the code for displaying the table into a class method.  The IntTable class actually has two such methods.   The first asks the user to supply (as parameters) the table as well as the number of rows and columns in the table.

//File:    IntTable.java
public class IntTable
{
    //The user supplies table (x) and number of rows and columns
    public static void display (int [] [] x, int rows, int cols)
    {
        for (int row=0; row<rows; row++)
        {
            for (int col=0; col<cols; col++)
            {
                System.out.print ("\t" + x [row] [col]);
            }
            System.out.println();
        }
    }
   
    //The following recognizes that a two-dimensional
    // array is just a one-dimensional array (rows)
    //of one-dimensional arrays (columns)
    public static void display (int [] [] x)
    {
        //x.length is the number of rows in the table
        for (int row=0; row<x.length; row++)
        {
            //x[row].length is the number of columns (for that row)
            for (int col=0; col<x[row].length; col++)
            {
                System.out.print ("\t" + x [row] [col]);
            }
            System.out.println();
        }
    }
}

The next version of our table testing program employs the "just in time" instantiation we used with one-dimensional arrays.  That is, we won't instantiate (allocate memory) until after we know how many students and quizzes are to be represented.

//File:    TestTable1.java
import iostuff.*;
public class TestTable1
{
    public static void main (String [] args)
    {
        int [] [] score;
       
        System.out.print ("How many students are enrolled? ");
        int students = Keyboard.readInt();
       
        System.out.print ("How many quizzes were given? ");
        int quizzes = Keyboard.readInt();
       
        //Instantiate here          
     score = new int [students] [quizzes];
       
        for (int student = 0; student < students; student++)
        {
            System.out.println ("Enter scores for student " + (student+1));
            for (int quiz=0; quiz < quizzes; quiz++)
            {
                score [student] [quiz] = Keyboard.readInt();
            }
        }
       
       
        //The following sets up the headers for the table
        System.out.println ("The gradebook is as follows: ");
        System.out.print ("Quiz\t");
        for (int k=1; k<=quizzes; k++)
        {
            System.out.print (k + "\t");
        }
        System.out.println();
        System.out.println();
       
        //Choose either of the display() methods from the IntArray class.
        //IntTable.display (score);
        IntTable.display (score, students, quizzes);
    }
}

Multidimensional arrays can be ragged (as with DisplayObjectList). A two-dimensional array in java is just a one-dimensional array of one-dimensional arrays. The rows can be of variable length.  What follows is a new version of our TestTable program that follows that approach.

//File:    TestTable2.java
import iostuff.*;
public class TestTable2
{
    public static void main (String [] args)
    {
        int [] [] score;
       
        System.out.print ("How many students are enrolled? ");
        int students = Keyboard.readInt();
       
        /* EACH STUDENT HAS DIFFERENT NUMBER OF QUIZZES
        System.out.print ("How many quizzes were given? ");
        int quizzes = Keyboard.readInt();
        */
       
        //CREATE AN ARRAY OF ARRAYS
        score = new int [students] [];
       
        //NEED TO KEEP TRACK OF LARGEST NUMBER OF QUIZZES TAKEN
        //IN ORDER TO SET UP TITLE FOR DISPLAYED TABLE
        int maximum_quizzes = 0;
        for (int student = 0; student < students; student++)
        {
            System.out.println ("How many scores for student "
            + (student+1) + "?");
            int number_of_quizzes = Keyboard.readInt();
           
            //UPDATE MAXIMUM_QUIZZES
            if (number_of_quizzes > maximum_quizzes)
                maximum_quizzes = number_of_quizzes;
           
            //INITIALIZE ARRAY OF QUIZZES FOR THIS STUDENT
            score[student] =new int [number_of_quizzes];
           
            System.out.println ("Enter scores for student " + (student+1));
            for (int quiz=0; quiz<number_of_quizzes; quiz++)
            {
                score [student] [quiz] = Keyboard.readInt();
            }
        }
       
        System.out.println ("The gradebook is as follows: ");
        System.out.print ("Quiz\t");
       
        for (int k=1; k<=maximum_quizzes; k++)
        {
            System.out.print (k + "\t");
        }
        System.out.println();
        System.out.println();
       
        IntTable.display (score);
    }
}

>java TestTable2
How many students are enrolled? 2
How many scores for student 1?
2
Enter scores for student 1
98
100
How many scores for student 2?
3
Enter scores for student 2
60
89
91

The gradebook is as follows:
Quiz     1         2            3

            98       100
            60        89         91

Introduction to input file processing

Let us look at the details behind I/O (Input/Output) in Java.  We've been using the text authors' code for inputting data from Input Boxes and in some examples, older code to input from the keyboard. The Input Box code introduces complexities in dealing with a GUI that we will not consider. Let us look at keyboard input. The details of the readInt() method from that class are displayed in all their glory in the following:

    static boolean iseof = false;
    static int i;
    static String line;

    public static int readInt ()
    {
        if (iseof) return 0;
        System.out.flush();
        try
        {
            InputStreamReader istr = new InputStreamReader(System.in);
            BufferedReader input = new BufferedReader (istr);
            line = input.readLine();
        }
        catch (IOException e)
        {
            System.exit(-1);
        }
       
        if (line==null)
        {
            iseof=true;
            return 0;
        }
       
        i = Integer.parseInt (line);
//         i = new Integer(s.trim()).intValue();     EQUIVALENT
        return i;
    }

It's perhaps wise to take a step back and try to get a view of the big picture.  Input is accomplished in Java through the use of input streams.  Until now, we have been content to use an input stream attached to the standard input stream (the keyboard).  We want to expand our options to include input streams attached to files.  In Java, files are viewed as byte streams ending with an end-of-file marker.   So the first task is to convert the byte streams to characters.  The character stream is then buffered.  From this buffer, each line of input can be extracted as a String.  Finally, the String object can be parsed to yield the data type required.   Let's indicate this process as follows:

  1. file (stream of bytes)

  2. FileReader object (stream of chars)

  3. BufferedReader object (buffers input allowing access from RAM instead of file)

  4. readLine() message to Buffered Reader object returns String reference to first line of input

  5. parse String object to extract data of appropriate data type.

In the case of input from the keyboard, these steps are implemented as:

  1. System.in object attached to input stream from keyboard.

  2. InputStreamReader istr = new InputStreamReader(System.in);

  3. BufferedReader input = new BufferedReader (istr);

  4. line = input.readLine();

  5. i = Integer.parseInt (line);

Input Files

An exactly parallel development applies to input from files.  We would have the following steps:

  1. String fileName associated with text file.

  2. FileReader fr = new FileReader (fileName);

  3. BufferedReader inFile = new BufferedReader (fr);

  4. line = inFile.readLine();

  5. i = Integer.parseInt (line);

The only difference appears in step 2, where for files, a FileReader object is required rather than an InputStreamReader object.

Putting all of these ideas together, we were able to construct a method that reads a list of integers from a data file.

    //File:    IntFiles.java
    public static int readIn (String fileName, int [] list)
    {
        int count=0;
        try
        {
        //The FileReader converts byte stream to char stream
        FileReader fr = new FileReader (fileName);

        //The BufferedReader enables efficient buffering of stream
        BufferedReader inFile = new BufferedReader (fr);
        String line = inFile.readLine(); //readLine() is in BufferedReader class
           
        while (line != null)     //null is the pointer
               //that points nowhere-- when no more lines in file, line = null.
        {
        list [count] = Integer.parseInt (line);
        count++;
        line = inFile.readLine();
        }
           
                    inFile.close(); //not invoked if throws exception
        }
        catch (FileNotFoundException e)
        {
            System.out.println ("The file " + fileName + " was not found.");
        }
        catch (IOException e)
        {
        }
       
        return count;
    }

Where did that try and catch thing come from?  The construction of FileReader object can result in an error (called an I/O exception in Java).  For example, suppose the file doesn't exist?  Rather than accepting this state of affairs, Java requires the programmer to indicate awareness of the potential disaster and provide a response.  This is accomplished through the use of try-catch clauses.  The potentially offending code is enclosed in the try block while the response code is enclosed in a catch block.  Note that it is not required that the program actually respond to the exception, merely indicate that the programmer is aware of the possibility.  In the above example, should an IOException, e, be "thrown", our catch block is empty indicating that our program will take no action.

We will continue this next time, but you now should have enough to take a stab at combining what you have learned about arrays and the method to read integers.


Lab Exercise.  Implement one of the sorting algorithms we have discussed (Bubble sort, Shell Sort, or Quick Sort) and add it to our collection of list processing routines (NumberList.java). Test it first with data input from the keyboard. Then test it on a data file on disk that you make with notepad.

Input Files Continued

In the material above, we looked at the process of getting data from input files:

  1. String fileName associated with text file.

  2. FileReader fr = new FileReader (fileName);

  3. BufferedReader inFile = new BufferedReader (fr);

  4. line = inFile.readLine();

  5. i = Integer.parseInt (line);

Putting all of these ideas together, we were able to construct a method that reads a list of integers from a data file.

    //File:    IntFiles.java
    public static int readIn (String fileName, int [] list)
    {
        int count=0;
        try
        {
        //The FileReader converts byte stream to char stream
        FileReader fr = new FileReader (fileName);

        //The BufferedReader enables efficient buffering of stream
        BufferedReader inFile = new BufferedReader (fr);

        String line = inFile.readLine(); //readLine() is
              //in BufferedReader class
           
        while (line != null)     //null is the pointer
              //that points nowhere--
              //when no more lines in file,
              //line = null.
       {
       list [count] = Integer.parseInt (line);
       count++;
       line = inFile.readLine();
       }
           
       inFile.close(); //not invoked if throws exception
        }
        catch (FileNotFoundException e)
        {
            System.out.println ("The file " + fileName +
            " was not found.");
        }
        catch (IOException e)
        {
        }
       
        return count;
    }

We tried to read from a file and sort as a lab exercise:

Lab Exercise.  Implement one of the sorting algorithms we have discussed (Bubble sort, Shell Sort, or Quick Sort) and add it to our collection of list processing routines (NumberList.java). Test it first with data input from the keyboard. Then test it on a data file on disk that you make with notepad.

Let us look at that exercise the problem of doing a Shell sort from earlier in the semester. Here is a version of NumberList with both readIn and ShellSort:


//File:  NumberList.java
import iostuff.*;
import java.io.*;
public class NumberList                               
{
  public static int [] inputList ()  
  {
    System.out.print ("How many items in your list? ");
    int n=Keyboard.readInt();
    System.out.println(n);

    //allocate n memory locations for array x
    int [] x = new int [n];

    //fill the array x with values from the user
    for (int k=0; k<n; k++)
    {
      System.out.print ("Next number please: ");
      x [k] = Keyboard.readInt();
      System.out.println(x[k]);
    }
    System.out.println();
    return x;  //return the filled array x
  }            

  //The following method outputs to the screen an array of integers
  public static void display (int [] list)
  {
    for (int k=0; k<list.length; k++)               
    {
      System.out.println (list[k]);
    }
    System.out.println();
  }

  //The following method outputs to the screen a list of n integers                        
  public static void display (int [] list, int n)
  {
    for (int k=0; k<n; k++)
    {
      System.out.println (list[k]);
    }            
    System.out.println();
  }



      //SORTING ALGORITHMS

  //NOTE:  In the following, length is an instance variable
  //  associated with an array object.  It carries the
  //  the size of the array, NOT necessarily the number
  //  of items input to the array.              


  //The insertion sort resembles the process of placing cards
  //into a hand one at a time in order
  static void insertionSort (int [] list)
  {
    for (int i=1; i<list.length; i++)
    {
      //list[0], list[1], ..., list[i-1] are in order
      int currentItem = list[i];
      int j = i-1;
      while (j>=0 && list[j]>currentItem)  //stops when finds
                                           //a smaller item
      {
        //looking for place to put list[i]
        //so moving elements to the right
        //to make room
        list[j+1] = list[j];  //jth item --> j+1 slot
        j--;
      }
      //list[i] goes into the j+1 slot immediately to
      //right of smaller item
      list[j+1] = currentItem;
    }
  }

  static void swap (int [] list, int i, int j)
  {  
    int temp = list[i];     
    list[i] = list[j];
    list[j] = temp;      
  }



  static void selectionSort (int [] list)
  {
    for (int k=0; k<list.length-1; k++)   
    {
      int min = smallestIndex (list, k);        
      swap (list, k, min);  //swaps list[k] with list[min]
    }
  }


  //The following method finds the INDEX of the smallest      
  //item from item n to the end
  public static int smallestIndex (int [] list, int n)
  {  
    int smallIndex = n;
    for (int k=n+1; k<list.length; k++)
    {
      if (list[k] < list[smallIndex])
      {
        smallIndex = k;
      }
    }
    return smallIndex;
  }

  //The following method finds the smallest item in a list of integers
  public static int smallest (int [] list)
  {
    int smallestSoFar; 

    //The following focuses on smallestSoFar,
    //a variable that is compared with each item
    //in the list in succession.  As smaller items
    //are encountered, they are assigned to the variable
    //smallestSoFar.               
    smallestSoFar = list[0];
    for (int k=1; k<list.length; k++)
    {
      if (list[k] < smallestSoFar)
      {
        smallestSoFar = list[k];
      }
    }
    return smallestSoFar;
  }  



  //The following two methods average an array of numbers

  //The following averages int's
  public static int average (int [] a)
  {
    int sum=0;
    for (int k=0; k<a.length; k++)
    {
      sum = sum + a [k];
    }
    return sum/a.length;                
  }    

  //the following averages double's
  public static double average (double [] a)
  {
    double sum=0;
    for (int k=0; k<a.length; k++)
    {
      sum = sum + a [k];
    }
    return sum/a.length;
  }


  //The following calculates the standard deviation of an array of int's
  public static int sd (int [] a, int average)
  {                                
    int sum=0;       
    for (int k=0; k<a.length; k++)   
    {
      sum = sum + (a[k]-average) * (a[k]-average);
    }  
    return (int) Math.sqrt(sum/a.length);
  }    


  // The next two methods implement a Shell sort

  private static void sortComb(int [] a, int g){

       // This method sorts the subset of the array a    
       // consisting of elements separated by the gap g

              int i;
              boolean inorder=false;
              while (! inorder) {
                    inorder=true;                  
                    for (i = g; i < a.length; i += g) {
                           if (a[i-g] > a[i] ) {
                                  int temp;

                                  // exchange the out-of-order elements
                                  temp = a[i-g];
                                  a[i-g] = a[i];
                                  a[i] = temp;            
                                  inorder = false;
                           }
                    }
              }
       }                

       public static void ShellSort(int [] a ) {

       // This method uses sortComb to progressively bring
       // array a into order by sortsusing finer and finer combs
       // ending with an ordinary bubble sort.

              int gap = a.length/2;
              while ( gap > 0 ) {
                    sortComb(a, gap);                 
                    gap /= 2;
              }
       }

//Here is the simple file reading code

       public static int readIn (String fileName, int [] list) {
        int count=0;
        try
        {
        //The FileReader converts byte stream to char stream
        FileReader fr = new FileReader (fileName);

        //The BufferedReader enables efficient buffering of stream
        BufferedReader inFile = new BufferedReader (fr);
        String line = inFile.readLine(); //readLine() is in
                                         //BufferedReader class

        while (line != null)     //null is the pointer    
                                 //that points nowhere -- 
                                 //when no more lines in
                                 //file, line = null.
        {
        list [count] = Integer.parseInt (line); 
        count++;
        line = inFile.readLine();                         
        }

                    inFile.close(); //not invoked if throws exception
        }      
        catch (FileNotFoundException e)
        {
            System.out.println (
              "The file " + fileName + " was not found.");
        }
        catch (IOException e)
        {
        }

        return count;               
    }



}

In order to test our new code, we need a driver application:


//File:  TestNumberList.java
//Test of NumberList class with data from keyboard                

public class TestNumberList {                


  public static void main (String [] args)  
  {
    int [] number = NumberList.inputList();      

    System.out.println ("The list of numbers is: "  );
    NumberList.display(number);                 

    System.out.println ("The smallest number in the list is: "
      + NumberList.smallest (number));

    int avg = NumberList.average (number);
    System.out.println ("The average of the list of numbers is: "
      + avg);

    System.out.println ("The standard deviation of the list is: "
      + NumberList.sd (number, avg));

    NumberList.ShellSort (number);  
    System.out.println("The sorted list is: ");
    NumberList.display(number);

    }
}    

When we run this program, here is what we see:


>java TestNumberList
How many items in your list? 5
5
Next number please: 8
8
Next number please: 7
7
Next number please: 6
6
Next number please: 5
5
Next number please: 4
4

The list of numbers is:
8
7
6
5
4

The smallest number in the list is: 4            
The average of the list of numbers is: 6
The standard deviation of the list is: 1
The sorted list is:
4
5
6
7    
8    

Now we create a driver dprogram to test reading from a file:


//File: FileNumberList.java
//Test of NumberList class with data from a file


public class FileNumberList {


  public static void main (String [] args)
  {  
    //int [] number = NumberList.inputList();
    int [] number;
    int n;                
    n = Integer.parseInt (args[0]);
    number = new int[n];
    n = NumberList.readIn(args[1], number);

    System.out.println ("The list of numbers is: "  );
    NumberList.display(number);

    System.out.println ("The smallest number in the list is: "
      + NumberList.smallest (number));

    int avg = NumberList.average (number);
    System.out.println ("The average of the list of numbers is: "
      + avg);

    System.out.println ("The standard deviation of the list is: "
      + NumberList.sd (number, avg));

    NumberList.ShellSort (number);
    System.out.println("The sorted list is: ");
    NumberList.display(number);

    }
}    

We use an editor to create a text file named xxx with 10 numbers, one per line:


10
1
9
2
8    
3
7
4                                
6
5

When we run it, we need to tell the program how many numbers and the file in which they are held. We use the program argument list to do this. The first argument is array element 0:

>java FileNumberList 10 xxx The list of numbers is: 10 1 9 2 8 3 7 4 6 5 The smallest number in the list is: 1 The average of the list of numbers is: 5 The standard deviation of the list is: 2 The sorted list is: 1 2 3 4 5 6 7 8 9 10

Up to this point, we have read only one number per line. In order to read more than one number per line, we will need some mechanism to breakup the line into appropriate pieces ("tokens"). We created an alternative input method for the IntFiles class that allows the input of a table of integers with exactly three columns. The methods in java.util.StringTokenizer handle breaking up the line into tokens.

    //The following method requires that we
    // import java.util.StringTokenizer;
    public static int readInTable (String fileName, int [] [] list)
    {
        int row=0;
        int col=0;
        String s;
        try
        {

        //The FileReader converts byte stream to char stream
        FileReader fr = new FileReader (fileName);

        //The BufferedReader enables efficient buffering of stream
        BufferedReader inFile = new BufferedReader (fr);
        String line = inFile.readLine(); //readLine() is
                //in BufferedReader class
           
        while (line != null)
        {
        StringTokenizer tokenizer = new StringTokenizer(line);
                 for (int k=0; k<3; k++)
        {
        s=tokenizer.nextToken();     //moves to
       //next integer on the line

        list [row] [k] = Integer.parseInt (s);
        }    
        row++;
        line = inFile.readLine();
        }
           
                     inFile.close(); //not invoked if throws exception
               
        }
        catch (FileNotFoundException e)
        {
            System.out.println ("The file " + fileName
       + " was not found.");
        }
        catch (IOException e)
        {
            System.out.println (e);
        }
        return row;
    }

Notice that we have introduced a new actor here, namely an object of the StringTokenizer class.  This is necessary because each line of the file contains not just one integer, but three of them.  Since line is a String object, it blurs the distinction between the three integers embedded in it.  The ability to extract each of those integers is possessed by objects of the StringTokenizer class.

We modified our original TestIntegerList driver program to test this new input method on table.txt.  Try it yourself.

//File:    TestTable.java
import iostuff.*;
public class TestTable
{   
    public static void main (String [] args)
    {
        int [] [] table = new int [8][5];

        String file = "table.txt";
       
        int n=IntFiles.readInTable (file, table);
       
       
        System.out.println ("The number of rows in the table is: " + n);
       
        for (int row=0; row<n; row++)
        {
            for (int col=0; col<3; col++)
            {
                System.out.print (table [row] [col] + "\t");
            }
            System.out.println();
        }
    }
}


Lab Exercise.  Expand the above readInTable() method to allow for a variable number of columns.  I think this would require the following signature:

public static int [] readInTable (String fileName, int [] [] list)

Note that the return type of the function is now int[] rather than simply int.  That's because we would like the function to tell us the number of columns as well as the number of rows.  So, the concluding statements in our modified method might be:

int [] temp = new int [2];
temp [0] = row;
temp [1] = col;

return temp;

HINT:  Look up the countTokens() method in the StringTokenizer class.

Have fun!  If you'd like to see Prof. Steinmetz's version, see the readTable() method in the updated IntFiles.java source code.

Output Files

Now we'll see how to send output to a file, rather than to the screen.

  1. PrintWriter object (converts a String to a stream of chars)

  2. BufferedWriter object (offers staging area for char stream)

  3. FileWriter object (converts char stream to byte stream)

  4. file receives byte stream from FileWriter object

The corresponding steps in your Java program (in reverse order) are:

  1. String fileName associated with text file.

  2. FileWriter fr = new FileWriter (fileName);

  3. BufferedWriter buffer = new BufferedWriter (fr);

  4. PrintWriter out = new PrintWriter (buffer);

  5. out.println (data);

  6. out.close();

As with input, we will have to arrange to catch exceptions.

Output Files

The last time we met, we described the process of converting input files from streams of bytes to appropriate data types.  Tonight we'll see how to reverse the process and send output to a file, rather than to the screen.

  1. PrintWriter object (converts a String to a stream of chars)

  2. BufferedWriter object (offers staging area for char stream)

  3. FileWriter object (converts char stream to byte stream)

  4. file receives byte stream from FileWriter object

The corresponding steps in your Java program (in reverse order) are:

  1. String fileName associated with text file.

  2. FileWriter fr = new FileWriter (fileName);

  3. BufferedWriter buffer = new BufferedWriter (fr);

  4. PrintWriter out = new PrintWriter (buffer);

  5. out.println (data);

  6. out.close();

Our first example mimicks the familiar "Hello World" program, sending output to a file rather than to the screen.

//File:    Hello0.java
import java.io.*;
import iostuff.*;

class Hello0
{
       public static void main (String [] args)
        {
            System.out.print ("What's the name of your file? ");
            String fileName = Keyboard.readString();
       
            //FileWriters convert char streams to byte streams
            FileWriter fr = new FileWriter (fileName);
       
            //PrintWriters convert Strings to char streams                
            PrintWriter out = new PrintWriter (fr);
       
            out.println ("Hi there gang!!. Ain't this great?");
            out.close();
        }
}

An attempt at compiling this program generates the following error message:

>javac Hello0.java
Hello0.java:12: Exception java.io.IOException must be caught, or it must be declared in the throws clause of this method.
FileWriter fr = new FileWriter (fileName);
^
1 error

As we discussed last time, the creation of I/O objects can generate I/O exceptions.  Java requires that we check for these exceptions.  So the following modified version works just fine:

//File:    Hello.java
import java.io.*;
import iostuff.*;

class Hello
{
       public static void main (String [] args)
        {
            System.out.print ("What's the name of your file? ");
            String fileName = Keyboard.readString();
       
           try
            {
                //FileWriters convert char streams to byte streams
                FileWriter fr = new FileWriter (fileName);
           
                //PrintWriters convert Strings to char streams                
                PrintWriter out = new PrintWriter (fr);
           
                out.println ("Hi there gang!!. Ain't this great?");
                out.close();    //NOTE:  Important to close output files after completing output.
                                 //              Otherwise you can lose some or all of your data

            }
           catch (IOException e)
            {
            }
    }
}

>javac Hello.java

>java Hello
What's the name of your file?
hello.txt

>type hello.txt
Hi there gang!!. Ain't this great?

This is probably a good time to mention an alternative to the try-catch clause.  Checking for exceptions is much like a game of hot potato.  If you don't provide for the I/O exceptions, then you can throw it to the calling program.  The following is an acceptable rewrite of the previous Hello program.

//File:    Hello2.java
import java.io.*;
import iostuff.*;

class Hello2
{
    public static void main (String [] args) throws IOException
    {
        System.out.print ("What's the name of your file? ");
        String fileName = Keyboard.readString();
       
        //FileWriters convert char streams to byte streams
        FileWriter fr = new FileWriter (fileName);
       
        //PrintWriters convert Strings to char streams
        PrintWriter out = new PrintWriter (fr);
       
        out.println ("Hi there gang!!. Ain't this great?");
        out.close();
    }
}

Finally, output to a file can be buffered.  That is, rather than sending data items one by one to the file (which is time consuming), we can collect data in RAM ( in an area called a buffer) and occasionally send data to the file.  This is accomplished through the BufferedWriter class as follows:

//File:    BufferedHello.java
import java.io.*;
import iostuff.*;

class BufferedHello
{
    public static void main (String [] args) throws IOException
    {
        System.out.print ("What's the name of your file? ");
        String fileName = Keyboard.readString();
       
        //FileWriters convert char streams to byte streams
        FileWriter fr = new FileWriter (fileName);
       
        BufferedWriter buffer = new BufferedWriter (fr);
       
        //PrintWriters convert Strings to char streams
        PrintWriter out = new PrintWriter (buffer);
       
        out.println ("Hi there gang!!. Ain't this great?");
        out.close();
    }
}

Command Line Arguments

In solving one of the lab problems last time, we made use of command line arguments, answering the implicit question:

In the line    public static void main (String [] args)    whats the String [] args for?

The answer is that the parameter "String [] args" provides a way to communicate information to the program from the command line.  Let's look at the following example:

//File:    TestArgs.java
import iostuff.*;
public class TestArgs
{   
       public static void main (String [] args)
        {
            System.out.println ("args [0] = " + args [0]);
            System.out.println ("args [1] = " + args [1]);
            System.out.println ("args [2] = " + args [2]);
        }
}

>java TestArgs How are you?
args [0] = How
args [1] = are
args [2] = you?

Clearly, String [] args represents an array of Strings. By adding a sequence of Strings to the command line, as we just did, the array is filled with those Strings.  In fact, this provides an effective way for the user to inform the program about files.

//File:    HelloArgs.java
import java.io.*;
import iostuff.*;

class HelloArgs
{
    public static void main (String [] args) throws IOException
    {
//            Don't need to ask the user for file name.   
//            System.out.print ("What's the name of your file? ");
//            String fileName = Keyboard.readString();
       
            //FileWriters convert char streams to byte streams
            FileWriter fr = new FileWriter (args[0]);
           //Pass the filename through the command line argument

            //PrintWriters convert Strings to char streams
            PrintWriter out = new PrintWriter (fr);
       
            out.println ("Hi there gang!!. Ain't this great?");
            out.close();
    }
}

>java HelloArgs output.txt

>type output.txt
Hi there gang!!. Ain't this great?

File Output of Tables

It's one thing to output a String to a file, but how about something a bit more complicated.  The following code has been added to our IntFiles class:

    //The following function outputs a list of n ints, one number per line,
    //to fileName

    public static void writeOut (String fileName, int [] list, int n)
    {
           try
            {
                FileWriter fr = new FileWriter (fileName);
                BufferedWriter buffer = new BufferedWriter (fr);
                PrintWriter out = new PrintWriter (buffer);

               for (int row=0; row<n; row++)
                {
                    out.println (list[row]);
                }
           
                out.close();
            }
           catch (IOException e)
            {
            }
    }

Finally, the following function outputs a table of ints.

    //The following function outputs a table of ints containing r rows and c columns
    //to fileName

    public static void writeOutTable (String fileName, int [] [] list, int r, int c)
    {
           try
            {
                FileWriter fr = new FileWriter (fileName);
                BufferedWriter buffer = new BufferedWriter (fr);
                PrintWriter out = new PrintWriter (buffer);

                for (int row=0; row<r; row++)
                {
                   for (int col=0; col<c; col++)
                    {
                        out.print (list [row] [col] + "\t");
                    }
                    out.println();
                }
           
                out.close();
            }
           catch (IOException e)
            {
            }
    }

Finally, we examine a possible solution to the Exercise from last time.

At Home Exercise.  Expand the above readInTable() method to allow for a variable number of columns.  I think this would require the following signature:

public static int [] readInTable (String fileName, int [] [] list)

Note that the return type of the function is now int[] rather than simply int.  That's because we would like the function to tell us the number of columns as well as the number of rows.  So, the concluding statements in our modified method might be:

int [] temp = new int [2];
temp [0] = row;
temp [1] = col;

return temp;

In fact, the following represents a solution (added to IntFiles.java):

    //The following function inputs a table of ints from fileName
    //and returns the number of rows and columns
    public static int [] readTable (String fileName, int [] [] list)
    {
           int row=0;
           int col=0;
            String s;
           try
            {
                //The FileReader converts byte stream to char stream
                FileReader fr = new FileReader (fileName);

                //The BufferedReader enables efficient buffering of stream
                BufferedReader inFile = new BufferedReader (fr);

                String line = inFile.readLine(); //readLine() is in BufferedReader class
           
               while (line != null)
                {
                    StringTokenizer tokenizer = new StringTokenizer (line);
                   int ncols = tokenizer.countTokens();
                    //countTokens() is member function in StringTokenizer class.  It's perfect for this application.

                   for (col=0; col<ncols; col++)    //Don't need to specify 3 here
                    {
                        s=tokenizer.nextToken();
                        list [row] [col] = Integer.parseInt (s);
                    }
                    row++;
                    line = inFile.readLine();
                }
           
                inFile.close(); //not invoked if throws exception
               
            }
           catch (FileNotFoundException e)
            {
                System.out.println ("The file " + fileName + " was not found.");
            }
           catch (IOException e)
            {
                System.out.println (e);
            }
       
            int [] temp = new int [2];
            temp[0]=row;
            temp[1]=col;
       
            return temp;

        }

Putting it all together, a driver for this function might look as follows:

//File:    TestTableArgs.java
import iostuff.*;
public class TestTableArgs
{   
    public static void main (String [] args)
    {
           int [] [] table = new int [8][5];

            String infile = args[0];
            int [] results = IntFiles.readTable (infile, table);
       
           int rows = results [0];
            int cols = results [1];
       
            String outfile = args[1];
            IntFiles.writeOutTable (outfile, table, rows, cols);
        }
}