As we saw last
section, the input and output streams
of the IO package
follow a
logical naming scheme related
to whether they are input or output classes , what
function
they
serve, and whether they deal with 8 or 16-bit primitive
types.
// in or out, 8 or
16 & function
This group further
subdivides
to low and high level streams.
Low level streams open on data sources
or
destinations,
while high level streams open on low-level streams. (High
and low
level are also called high and low order streams.)
//
high level streams open on low-level streams
In addition to
these stream classes there are special classes
like the
File class, RandomAccessFile and StreamTokenizer
that have unique abilitites. The
package also houses the
exception classes that are related directly to input and
output
functions.
//
package
holds
special function classes & exception classes
The tables below
categorize the IO classes along the lines
just described.
Notice
there is not perfect symetry across
the categories. Sometimes stream
classes fall
into one or
two categories instead of having a version in all four
general
divisions.
Categorizing the Classes
of the java.io Package
The following tables
list classes of the java.io package in
categories
based on different classification criteria.
8-Bit
Input
/
Output Stream Classes
Low Level InputStreams | Low Level OutputStreams |
FileInputStream ByteArrayInputStream PipedInputStream |
FileOutputStream ByteArrayOutputStream PipedOutputStream |
High Level InputSteams | HighLevel OutputStreams |
BufferedInputSteam DataInputStream PushbackInputStream |
BufferedOutputStream DataOutputStream PrintStream |
16-Bit Reader / Writer
Classes
Low Level Readers | Low Level Writers |
FileReader CharArrayReader PipedReader StringReader |
FileWriter CharArrayWriter PipedWriter StringWriter |
High Level Readers | High level Writers |
BufferedReader PushbackReader LineNumberReader |
BufferedWriter PrintWriter |
Specialty Classes
Unique Stream Classes | Non-stream classes | |
SequenceStreamReader InputStreamReader OutputStreamWriter ObjectInputStream ObjectOutputSteam StreamTokenizer |
File FileDescriptor
RandomAccessFile |
Low-Level
Stream Classes
First we survey the low-order streams. These open
on specific
sources and destinations.
These include files, arrays, strings
and piped
streams.
FileInputStream FileOutputStream FileReader FileWriter |
-
a
byte-based
input stream that reads from a file -a byte-based output stream that writes to a file - a character-based stream for reading from a file - a character-based stream for writing to a file |
ByteArrayInputStream ByteArrayOutputStream CharArrayReader CharArrayWriter |
-
takes
input
from a byte array or an array subset - writes to a byte array or an array subset - reads characters from a character array - writes characters to a character array |
No
corresponding 8-bit streams -note-> StringReader StringWriter |
// StringBufferInput/OutputStream deprecated // problems translating characters to bytes - reads characters from a String supplied to constructor - writes characters to a string buffer (not to String) |
PipedInputStream PipedOutputStream PipedReader PipedWriter |
-
reads
bytes
from an attached piped output stream - writes bytes to an attached piped input stream - reads characters written to an attached piped writer - writes characters to an attached piped reader |
// Piped streams are used for
inter-thread communication in multithreaded environments.
// Piped streams can replace
wait( )/notify( ) in the classic producer/consumer scenario.
High-Level
Stream
Classes
//
Take their input from other streams
BufferedInputStream BufferedOutputStream BufferedReader BufferedWriter |
- uses an internal array to buffer data read from source - collects data in buffer until full then writes in one operation - uses an internal array to buffer data read from source - collects data in a buffer until full then writes in one operation |
DataInputStream DataOutputStream No Reader version No Writer version |
-
reads
input
stream bytes & converts to Java
primitives - converts Java primitive to output stream bytes // no need for 16 bit version. // just deals with primitives and bytes |
LineNumberReader No InputStream or Writer counterpart |
-
maintains
a
count of lines read, - sub-classes BufferedReader // LineNumberInputStream is deprecated |
PrintStream PrintWriter Outbound Classes |
-
supports
easy
writing of text to output - like PrintStream but defines char based methods // No input counterparts |
PushbackInputStream PushbackReader No OutputStream or Writer counterpart |
-
allow
byte(s)
read to be pushed back to source - allow char(s) to be pushed back to source // both use internal stacks |
SequenceInputStream |
- concatenates two or more input streams |
InputStreamReader OutputStreamWriter Adapter classes |
- reads bytes and converts them to chars - takes chars and converts to a stream of bytes // Convert between the 8 and 16-bit stream types |
ObjectInputStream ObjectOutputStream No Reader/Writer Types |
-
reads
serialized
objects from stream - writes serialized objects to stream // objects are serialized to stream in bytes |
The stream classes described in these tables follow for the most
part
the same patterns of usage we described in our earlier note.
We also will look at a few of the more specialized classes like File,
RandomAccessFile and StreamTokenizer in the following examples.
To write a String or an array to file, FileWriter
offers easy to
use, overidden versions
of write that take an array or a String.
Commented
out is an alternate way of writing
a String to file
using the FileOutputStream class
in conjunction with PrintWriter.
Sample showing
FileWriter 's Economic Use of Code
Form of a read loop
Often you'll
want to read everything from a file into a program.
A loop is the
easy way to do this. In streaming though, one
needs to be careful to
not skip a
byte being read when doing
an evaluation to see if it is the last byte. A way to
avoid this
problem is to assign return value of the the byte read( ) function
to a variable inside
the while loops
argument area.
In the next example
the inner brackets allows the assignment
to
take place before the evaluation occurs. The -1 signifies the
end of
file or EOF.
Example
while ( ( c =
in.read( ) ) ! = -1 )
System.out.println( c );
The following short code sample shows the while loop being
used in this
form. (Try with a file called Zee.txt with the String
ABCDEFG stored in it.)
While Loop read( ) Sample
import
java.io.*;
class
ReadLoop {
public static void main(String[]args){
int symbol=0;
FileInputStream fis=null;
try{
fis=new FileInputStream("Zee.txt");
while( (symbol=fis.read( ))!= -1){
System.out.print((char)symbol
+ "-");
}
}
catch(FileNotFoundException fnfe){}
catch(IOException io){}
}
}
OUTPUT
A-B-C-D-E-F-G-H-I-J-
To demonstrate why it is neccessary to capture each read
before testing for the EOF symbol we can used the following
code which effectively loses one turn on each read as the
loop evaluation loses one of the characters read.
Sample
Showing
Reading
Without Character Capture
import
java.io.*;
class ReadLoop {
public static void main(String[]args){
int symbol=0;
FileInputStream fis=null;
try{
fis=new FileInputStream("Zee.txt");
while(fis.read( ) != -1){
symbol=fis.read( );
System.out.print((char)symbol + "-");
}
}
catch(FileNotFoundException fnfe){}
catch(IOException io){}
}
}
OUTPUT
B-D-F-H-J-
Every
second
letter
has been sacrificed in the
while loop to test for -1.
In Java, file meta-data, (information about
a
given
file but
not it's contents) is
returned by methods of the File class.
The File class doesn't itself
do any IO.
What follows is a
summary of some of the work that the File class can
do
along with a
table describing of some of it's methods.
Some Tasks File Class Can Perform
1)
returns a File object which a file can be opened on.
2) tests if a file exists, or can be read/write
3) tests whether a File object represents a file or a
directory
4) returns # bytes and when file was last modified
5) has methods to create and delete files and
directories
Some of File's
methods //
see jdk API for fuller descriptions
public int compareTo (java.io.File) |
lexigraphical comparison < = = > |
public static File createTempFile (String1, String2, File) throws IOException |
File
is dir
where temp file will be created String1-->file name prefix String2--> suffix (min.3 chars long) |
public void deleteOnExit( ); |
marks
file
to
be deleted when program ends (can't rescind call) |
public boolean mkdir( ) |
creates one directory |
public boolean mkdirs( ) |
as
mkdir(
)
plus any needed parent directories |
String[] list( ); | returns
a
String array [of
files & dirs]
contained in the dir from which the method has been invoked |
File[] listFiles( ); | same as list( ) but returns File objects not String |
listRoots( ) | lists
the
available
filesystem roots (i.e. "A:\", "C:\" etc.) |
URL
toURL(
)
throws MalformedURLException |
puts
File
object
into URL form
(file:///
something) (if the File object is a directory it end's in a slash "/") |
public boolean createNewFile( ) throws java.io.Exception |
creates
a
new
file if it doesn't
already exist
and returns boolean if a new file has been created. |
File Class in Code
The
first
example
is short and is provided to show the
mkdirs( ) method in action.
File mkdirs( ) method in Code
import
java.io.*;
class MK{
public static void main(String[]args){
File f =new File("X/Y/Z");
System.out.println("All Directories Created: " + f.mkdirs());
}
}
OUTPUT
>
java
MK
All Directories Created: true
A Bigger File Class Example
The next code
sample class shows a number of File
methods. It uses
a
recursive method for traversing
a directory that can be found in the 'Java Certification
Study Guide' written by Heller & Roberts. A second
functionality
is added to show information that the File
class can provide for a
single file.
The
java.util package supplies the Date class which can
be used to return the number of milliseconds that have
transpired since the beginning of 1970. Between the
getTime( ) method of the Date class and the File class
method, lastModified( ),the amount of time that has
transpired
since the last time the file was modified can
be deduced. Notice the
file report
will only say a file
exists if it is found in the same directory.
A Fix that was Fixed There
is a fix in
the following code that doesn't seem to be necessary in more recent
releases of Java. Scrolling would sometimes leads to
bad painting.
To But
now it seems it has no effect, perhaps because systems are quicker now.
|
File Class Code Sample
import
java.awt.*;
import java.io.File;
import javax.swing.*;
import java.awt.event.*;
import java.io.*;
import java.util.*;
import javax.swing.event.*;
public
class
NewLister
extends JFrame
implements ActionListener,ChangeListener{
JTextField tf1,tf2;
JTextArea ta;
// main
instantiates class
public static void main(String args[]){
NewLister lister=new NewLister();
}
// visual build in constructor
NewLister(
){
ta=new JTextArea();
JScrollPane sp=new JScrollPane(ta);
JViewport view=sp.getViewport();
view.addChangeListener(this);
ta.setFont(new Font("Monospaced",Font.PLAIN,12));
getContentPane().add(BorderLayout.CENTER,sp);
JPanel
dir=new
JPanel();
dir.setLayout(new FlowLayout(FlowLayout.LEFT));
JLabel dirlabel=new JLabel("Enter a
directory",SwingConstants.CENTER);
dir.add(dirlabel);
tf1=new JTextField(" Enter a
directory
", 52);
tf1.addActionListener(this);
tf1.setFont(new
Font("Monospaced",Font.PLAIN,14));
dir.add(tf1);
JLabel
fill=new
JLabel("
");
dir.add(fill);
dir.add(tf1);
getContentPane().add(BorderLayout.NORTH,dir);
JPanel file=new JPanel();
file.setLayout(new FlowLayout(FlowLayout.LEFT));
JLabel filelabel=new JLabel("Pick a file in the
directory ",SwingConstants.CENTER);
tf2=new JTextField(" Enter a file name for
file details ",50);
tf2.addActionListener(this);
tf2.setFont(new
Font("Monospaced",Font.PLAIN,14));
file.add(filelabel);
file.add(tf2);
JLabel fill2=new
JLabel("
");
dir.add(fill2);
getContentPane().add(BorderLayout.SOUTH,file);
setSize(700,450);
setVisible(true);
}
//action method
public void actionPerformed(ActionEvent ae){
if(ae.getSource().equals(tf2)){
String name=tf2.getText();
File f2=new File(name);
if(f2.isDirectory()){
tf2.setText("Doesn't
exist or is a directory");
}
else{
try{
String report=
" The file exists:
" + f2.exists() +
"\n The absolute
path is: " + f2.getAbsolutePath() +
"\n The canonical
path is: " + f2.getCanonicalPath( ) +
"\n A hashCode for
this pathname: " + f2.hashCode() +
"\n This is a
directory:
" + f2.isDirectory() +
"\n This is a
file:
" + f2.isFile( ) +
"\n This is a
hidden
file: " + f2.isHidden() +
"\n This file was
last modified(in days): " +
lastMod(f2.lastModified())
+
"\n This file
length
is: " + f2.length();
ta.setText(report);
}
catch(IOException
io){
io.printStackTrace();
}
}
}
if(ae.getSource().equals(tf1)){
ta.setText("");
String name=tf1.getText();
File f2=new File(name);
if(!f2.isDirectory()){
System.out.println("Doesn't
exist or not a path");
ta.setText("Doesn't exist or
not a path");
}
else{
recurse(f2,0);
repaint();
}
}
}
// change listener
fixes
painting
in
slow systems
public void stateChanged(ChangeEvent e){
ta.repaint( );
}
// a method to calculate
time elapsed from day last modified to today
long lastMod(long mls){
long today =new Date(
).getTime(
);
System.out.println(today);
long days= (today - mls) /
(1000
* 60 * 60 * 24);
System.out.println(mls);
System.out.println(days);
return days;
}
// Heller & Roberts
recurse(
) method from the Java Certification Study Guide
void recurse(File dirfile, int depth){
String contents[]=dirfile.list( );
for (int i=0; i<contents.length; i++) {
for (int spaces=0; spaces<depth;
spaces++)
ta.append("
");
ta.append(contents[i] + "\n");
File child=new File(dirfile,contents[i]);
if (child.isDirectory())
recurse(child,depth+1);
}
}
}
FileDescriptor // just for reference
Instances of the
file descriptor class serve as an 'opaque' handle
to
the underlying
machine-specific structure representing an open
file, an open socket,
or another
source or sink of bytes. The main
practical use for a file descriptor
is to create a
FileInputStream or
FileOutputStream to contain it.
JDK Documentation
states that applications
should not create
their own file descriptors.
RandomAccessFile is
a unique and powerful class. Where most
of the stream
classes implement input or output interfaces, the
RandomAccessFile
implements
both functionalities. Because of
the way it is built it cannot be
layered
in streams
like other stream
classes. RandomAccessFile has the reading and
writing
methods
of DataInputStream and DataOutputStream combined.
RandomAccessFile
has a file pointer that can be used to find a
location inside a file at any offset point.
//
RandomAccessFile can create files
RandomAccessFile
can create files just by specifying them in it's
constructor,
whether the file existed before or not. (This is a capability
of
FileOutputStream
and FileWriter as well. ) The constructor also
specifies if the file
is read or read
write. The constructors can take
files specified by their String name
or as File
objects. The mode is
read or read-write and is specified with String
literals,
"r" or "rw".
Constructor Form 1
RandomAccessFile(File
file,
String
mode)
// mode is "r" or "rw"
,
takes a file object
Constructor Form 2
RandomAccessFile(String
name,
String
mode)
//
takes
a
String representation of a File name.
The RandomAccessFile class
defines the methods, seek( ) and
getFilePointer(
) to point at locations inside a file. The seek( )
method
takes a long value and sets the position of the
file pointer
so a specific location
in a file can be accessed. The getFilePointer(
)
method will return a long value
representing the location where the
file pointer is located.
The seek( ) method will throw an IOException if the a
value less
than zero is selected or a value whichis greater than the length
of the file.
Special RandomAccessFile methods
void seek( long position )
// sets the position of the file pointer.
long getFilePointer(
)
// returns the current location of the file pointer
The following
RandomAccessFile Code sample creates a text
area in a
JFrame.
A RandomAccessFile object is created on a
File object. The File object
is opened
on a file on the system.
The length method of the File class is used
to control the
number
of times bytes are read from the file. In each turn through
the loop
the
byte read is appended to the text displayed in the text area.
To show
how seek( )
works, arbitrary positions in the stream are
printed to console.
// seek uses offset values reads absolute positions
RandomAccessFile Code Sample
import
java.io.*;
import javax.swing.*;
import java.awt.event.*;
import java.awt.*;
class Random{
public
static
void main(String[]
args){
int i=0;
RandomAccessFile raf=null;
if(!(args.length>0)){
System.out.println
("Enter a file
name
at the command line");
System.exit(0);
}
String name=args[0];
JFrame frame=new JFrame( );
JTextArea
jta=new
JTextArea( );
jta.setFont(new
Font("Monospaced",Font.PLAIN,12));
frame.getContentPane().add(jta);
try{
File file=new
File(name);
raf=new
RandomAccessFile(file,"rw");
for(int
z=0;z<file.length();z++){
i=raf.readByte();
// this bit of logic keeps the index numbers visually formatted
if(z<10)
jta.append(" " +z+
" : " + (char)i + "\n");
else
jta.append( z +
" : " + (char)i + "\n");
}
}
catch(FileNotFoundException
fnfe){
fnfe.printStackTrace();
}
catch(IOException io){
io.printStackTrace();
}
//
these writes are back at the command line console
// note the file pointer is set to one more than the seek value
try{
raf.seek(0);
System.out.println("" +(char)raf.readByte());
System.out.println("File Pointer is at: " +
raf.getFilePointer());
raf.seek(5);
System.out.println("" +(char)raf.readByte());
System.out.println("File Pointer is at: " +
raf.getFilePointer());
raf.seek(12);
frame.setVisible(true);
System.out.println(""
+(char)raf.readByte());
System.out.println("File Pointer is at: " +
raf.getFilePointer());
raf.seek(14);
System.out.println("" +(char)raf.readByte());
System.out.println("File Pointer is at: " +
raf.getFilePointer());
}
catch(IOException io){}
frame.setSize(200,500);
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
}
StreamTokenizer parses input into tokens, usually
from inside
a while loop, calling
nextToken( ) until the end of the input is
reached.
Tokens are defined groups of
characters such as
words. The int returned by
the nextToken( ) method describes
the type of
the next token, (as described by static fields
such
as TT_NUMBER, TT_WORD,
TT_EOL, & TT_EOF) Many
methods are
available to recognize various user-specified
characters.
StreamTokenizer is
particularly
suited for parsing
Java, C or
C++. Notice in the following table summary
of
StreamTokenizer's fields that
non-static fields are lowercase.
StreamTokenizer's Field Summary
//
from
the JDK documentation
double nval | If the current
token is a
number, this field contains the value of that number. |
String sval | If the current
token is a
word token, this field contains a string giving the characters of the word token. |
static int TT_EOF | A constant
indicating that
the end of the stream has been read. |
static int TT_EOL | A constant
indicating that
the end of the line has been read. |
static int TT_NUMBER | A constant
indicating that
a number token has been read. |
static int TT_WORD | A constant
indicating that
a word token has been read. |
int ttype | After a call to
the nextToken
method, this field contains the type of the token just read. |
StreamTokenizer has a deprecated constructor that
opens
on an InputStream
object and preferred constructor that
opens on
a Reader class. The recommended
way to convert
from input stream to a reader
is as follows.
A StreamTokenizer Opening on a
BufferedReader
Reader r = new BufferedReader(new
InputStreamReader(is));
StreamTokenizer st = new StreamTokenizer(r);
This is similar to how we used an input to send to console.
The
technique is used below in the
code sample. The code opens
on a file
with a FileInputStream which is then
set to standard
input. A Stream Tokenizer is
then opened on the buffered reader
which is opened on the standard input. The
stream
tokenizer's
nextToken( ) method is used
to process each token until the end
of file
symbol
is reached. If the current token is
a word token, the
'sval' field of StreamTokenizer
returns a String representing the
word
token.
Create a file 'Read.txt' that has words among numbers.
The
program will filter word tokens from the text.
/* Today there is a new 45 combination that 7 is top
secret 99
and will not be disclosed 22 to anyone! */
StreamTokenizer
Code
Sample
Following is a more
elaborate version of the program that
filters both numerical values and word values and recomposes
them in groups. In this case an imaginary swiss bank account
number is interspersed inside a text file. The program retrieves
the account number from the file.
// Save the
following text in a file called 'SwissCheese' . If you were
really
// hiding a number somewhere your might start
with a much
larger text body
// that is prescreened for extraneous number
characters.
/* This text
4 hides a 1 hidden 2 account
9 9
number 0 for a Swiss 3 bank 5 account. */
import java.io.*;
class SwissTokens{
public static void main(String[]args){
int token;
// the
array size is pre-set to the size needed for the imaginary swiss
account
int[] account = new int[8];
try{
FileInputStream fi =
new FileInputStream("SwissCheese");
System.setIn(fi);
BufferedReader in=new
BufferedReader
(new
InputStreamReader(System.in));
StreamTokenizer st=new
StreamTokenizer(in);
st.parseNumbers();
int i = 0;
while( (token=st.nextToken( ))!=
StreamTokenizer.TT_EOF)
{
//
ttype
is a non-static variable
if
(st.ttype==StreamTokenizer.TT_NUMBER)
{
account[i]= (int)Math.round(st.nval);
i++;
}
if (st.sval!=null)
System.out.print(st.sval + " ");
}
in.close( );
fi.close( );
System.out.print("\nThe number is: ");
for (int
j=0;j<account.length;j++){
System.out.print(account[j]);
}
System.out.println("");
}
catch(IOException io){
System.out.println("IO Exception");
}
}
}
OUTPUT
This text
hides a hidden account number for a Swiss bank account.
The number is: 41299035
StringTokenizer
There is also a StringTokenizer but it is in the java.util package and not a part of the java.io package It might be worth visiting for certain applications. The following example is from the JDK 1.3 documentation StringTokenizer
st = new StringTokenizer("this is a test"); |
SequenceInputStream provides a way for concatenating
two or
more streams together.
It's constructors take either an enumeration
or two streams. To use
the enumeration
technique, separate
FileInputStream objects are added to a Vector.
Then the Vector's
elements( ) method is called to return an enumeration. which is
then passed, in turn, as an argument to the SequenceInputStream
constructor.
The following
code sample
does all this, and then prints from the
resultant stream to prove the
three files have been
appended
together. You will have to supply the files if you wish to
test this
code.
//
supply Apples.txt, Orange.txt & Plum.txt
The Invasion of Generics & the 'enum' type with JDK 1.5
Generics have
invaded the typing system. You can expect the
compiler to be 'upset' if it is forced to determine object type at
runtime. The following code shows the new generic form applied
to Vector and the Enumeration type declaration.
There is also a new
enum
type added with JDK 1.5 as well,
which adds a major new data type to the language. It has put
the 'enum' keyword into full service in the Java language so
now we cannot call our enumeration an 'enum' as a result.
SequenceInputStream Code Example
import java.io.*;
a )
FileOutputStream
b) PipedInputStream
c) BufferedReader
d) ByteArrayOutputStream
2) Which of the following is not a high order class?
a) LineNumberReader
b) PipedReader
c) SequenceInputStream
d) DataInputStream
3) Which of the
following is not an 16-bit stream
a) PipedWriter
b) BufferedReader
c) LineNumberReader
d) ObjectOutputStream
4 ) True or False.
A File object can be created on a file name whether
an
underlying file exists on the system or not.
5) True or False.
If a RandomAccessFile is opened on a non-existent
file
in read and write mode the file described will be
created on the underlying
system.
6 )Which class
would be the best choice for writing ASCII characters
to
an array.
a)
ByteArrayOutputStream
b) ByteArrayInputStream
c) CharArrayWriter
d) CharArrayReader
Answer
the following questions with brief but complete statements.
1 ) Create a
command line program. Inside main
add the following.
File filo=new File("filo");
boolean alive=filo.exists( );
System.out.println("x is a file: " + alive);
Using the dir
command first check if a file is created on the
underlying
system.
a) Does the file
exist.
b) Does the creation of a File object create a file on the
underlying
system?
2) Create a file
with a sentence or two in it and enter it's
name into
the File constructor of the program you created
in number
1. What does the exists( ) method now indicate
regarding the file.
3) To the above
program create a RandomAccessFile that
opens on a non-existent file, something like
"ZZZ.txt".
Set the second
argument to read mode, "r". Will the
compiler
allow you to proceed?
Does the program run? What happens?
Check the underlying directory, was the file
created?
4) Repeat the
excercise in 3) this time setting the read-write
attribute to read
& write "rw". Will the
compiler allow you to
proceed?
Does the program run? What happens? Check the
underlying directory, was the file
created?
5) Now open
the RandomAccessFile in the two modes on an
existent
file.
a ) In read only mode did the program compile
and run?
b ) In read-write mode did the program compile
and run?
Optional
6) Create a program
that loads a file and uses File classes
length method,
and RandomAccessFile's seek( ) method to
create a second file
that stores the
characters of the first
file in reverse order. Prove the conversion was successful,
by opening on the
reverse ordered file and restoring it to it's
original condition.
How you might do this //
only read if needed!
You may find how
many characters there are using
String's
length( ) method. In a descending loop, pass the counter to
the seek method to get each character. You could create a
String to which each
character
was appended and then write
the String to file in one go. Alternatively
writing could be done
simultaneously from the loop to a FileOutputStream object.