File I/O. (10.3, 10.4).
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);
}
}