To complete the subjects we need to consider:
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
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
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:
file (stream of bytes)
FileReader object (stream of chars)
BufferedReader object (buffers input allowing access from RAM instead of file)
readLine() message to Buffered Reader object returns String reference to first line of input
parse String object to extract data of appropriate data type.
In the case of input from the keyboard, these steps are implemented as:
System.in object attached to input stream from keyboard.
InputStreamReader istr = new InputStreamReader(System.in);
BufferedReader input = new BufferedReader (istr);
line = input.readLine();
i = Integer.parseInt (line);
An exactly parallel development applies to input from files. We would have the following steps:
String fileName associated with text file.
FileReader fr = new FileReader (fileName);
BufferedReader inFile = new BufferedReader (fr);
line = inFile.readLine();
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.
In the material above, we looked at the process of getting data from input files:
String fileName associated with text file.
FileReader fr = new FileReader (fileName);
BufferedReader inFile = new BufferedReader (fr);
line = inFile.readLine();
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.
Now we'll see how to send output to a file, rather than to the screen.
PrintWriter object (converts a String to a stream of chars)
BufferedWriter object (offers staging area for char stream)
FileWriter object (converts char stream to byte stream)
file receives byte stream from FileWriter object
The corresponding steps in your Java program (in reverse order) are:
String fileName associated with text file.
FileWriter fr = new FileWriter (fileName);
BufferedWriter buffer = new BufferedWriter (fr);
PrintWriter out = new PrintWriter (buffer);
out.println (data);
out.close();
As with input, we will have to arrange to catch exceptions.
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.
PrintWriter object (converts a String to a stream of chars)
BufferedWriter object (offers staging area for char stream)
FileWriter object (converts char stream to byte stream)
file receives byte stream from FileWriter object
The corresponding steps in your Java program (in reverse order) are:
String fileName associated with text file.
FileWriter fr = new FileWriter (fileName);
BufferedWriter buffer = new BufferedWriter (fr);
PrintWriter out = new PrintWriter (buffer);
out.println (data);
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();
}
}
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?
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);
}
}