A8. The Java File I/O Classes


Note: This section is provided for information only and will not be presented as part of the course lectures. In this section, you will learn:
  • How to write bytes, primitive types, and text to a stream.
  • How to read bytes and primitive types from a stream.
  • How stream filters are combined to form more sophisticated streams.
  • How to perform random access I/O.
  • How to read from a URL.

Introduction

The Java language provides a set of predefined classes and objects for reading and writing data to and from files on a local file system. (Recall, however, that browsers generally prohibit applet file I/O for security reasons.) Both sequential and random access are possible. In this section, we will focus on the important I/O classes providing motivation for their existence, providing examples illustrating their common use, and describing their relationship to other I/O classes.

Predefined System I/O Objects

The Java language provides two static objects in class System for sending output to stdout and stderr:
static PrintStream out;
static PrintStream err;
Methods print() and println() are used to print ASCII text representations of their arguments (PrintStreams chop off the high byte when it prints. To print UNICODE, you need a PrintWriter instance. For example,
System.out.println("Goes to stdout with 
   newline");
System.out.print("Goes to stdout without
   newline");
System.err.println("Goes to stderr with
   newline");
The Java language also provides a static object in class System for reading input from stdin:
static InputStream in;
which is actually initialized to a BufferedInputStream. Method read() reads in a single character and returns -1 upon end of input:
int c;
try { c = System.in.read(); }
catch (IOException e) {}

File Objects

You can use File objects to obtain information about a file or directory. File objects differ from stream objects such as InputStream in that a File object is used to obtain information about a particular file or directory and is not used to read or write data. For example, to determine the length of a file:
String fileName = "data";
File f = new File(fileName);
System.out.println("file " + fileName + " is " +
      f.length() + " bytes long");
For another example, when the File object is associated with a file system directory, you can easily obtain the list of files in that directory:
String fileName = "examples";
File f = new File(fileName);
String[] dirListing = f.list();

Writing Output

The Java language provides class FileOutputStream for writing bytes, class FileWriter for writing characters, class DataOutputStream for writing primitive types, and classes PrintStream and PrintWriter for printing primitive types. FileOutputStream is a pure byte stream, FileWriter is a pure 16-bit char stream, DataOutputStream writes items of primitive type in binary form, and PrintStream and PrintWriter print items in human-readable form (i.e., ASCII or UNICODE, not binary). These classes are the most commonly used, and all are created either from a String filename or from a File object.

The following table summarizes the file output classes we will describe:

Summary of OutputStream/Writer Hierarchy
Class or Interface Description
OutputStream Generic byte output stream.
Writer Generic char output stream.
FileOutputStream Basic byte output stream.
FileWriter Basic char output stream.
FilterOutputStream Allow byte streams to be chained.
FilterWriter Allow char streams to be chained.
BufferedOutputStream Buffer an OutputStream.
BufferedWriter Buffer a Writer.
DataOutputStream Writes primitive types in binary.
PrintStream Prints primitives & objects in text form.
PrintWriter Prints primitives & objects in text form.

What is class FilterOutputStream for?

Because class FileOutputStream writes only bytes, the Java language provides the abstract class FilterOutputStream to enable you to write more complex types of output. The "filters" DataOutputStream and BufferedOutputStream, for example, can be chained and attached to a FileOutputStream to provide this enhanced output functionality.

These Java filters are analogous to UNIX shell pipe filters. You might think of the Java process of attaching DataOutputStream to FileOutputStream as being represented in UNIX by:

% echo 34 | DataOutputStream | FileOutputStream
The different Writer classes work in the same manner but work on the character-level, instead of the byte level. If you are working with text, you should use the Writer objects, if you are using Java 1.1.

FileOutputStream/FileWriter

FileOutputStream is the class to use if you want to write binary bytes, FileWriter is for chars. For example, the following code writes a byte array to a file called "junk".
byte[] someBytes = { 'a', 'b', 'c' };
FileOutputStream f;
f = new FileOutputStream("junk");
if ( f!=null )
{
   f.write(someBytes);
   f.close(); // free up system resource
}

DataOutputStream

By chaining the DataOutputStream to the FileOutputStream, you can write output that is more complex than bytes. If you have a primitive type, for example, that you want to write in binary in a portable manner, you can attach a DataOutputStream object to a FileOutputStream like this:
// write 34 in binary to a file called "junk"
// make a generic output byte stream
FileOutputStream f = 
   new FileOutputStream("junk");
// attach a DataOutputStream to it
DataOutputStream df = new DataOutputStream(f);
if ( df!=null )
{
   df.writeInt(34);
   df.close(); // free-up system resource.
   f.close(); // close in reverse order
}
The output may be portably read by DataInputStream.

PrintStream/PrintWriter

If you want to print to a text file, you can use a PrintStream object. For example:
FileWriter f = new 
  FileWriter("junk");

PrintWriter pf = new PrintWriter(f);

if ( pf!=null ) {
   pf.println(34);
   pf.println("an int is " + 34);
   pf.println(3.14159);
   pf.println("This is a string");
   pf.close(); // free up system resource.
   f.close(); // close in reverse order
}

BufferedOutputStream

Class BufferedOutputStream allows you to write bytes to a buffer without always doing an actual file system write. The buffer is written to the stream when the buffer is full, when you close() the stream, or when you manually flush() the buffer. You must attach a BufferedOutputStream object to an OutputStream object, and you can then use it like the OutputStream object.
// make a generic output byte stream
f = new FileOutputStream("junk");
// attach BufferedOutputStream 
bf = new BufferedOutputStream(f);
bf.write(34);

Reading Input

The Java language provides class FileInputStream for reading bytes, FileReader for reading characters, and class DataInputStream for reading primitive types. These classes are the most commonly used and are created from either a String filename or from a File object.

To read text items and convert them to primitive types, You will normally read characters into a String and then parse the String with methods such as Integer.parseInt() and Float.valueOf(). (Class StreamTokenizer is an alternative.)

FilterInputStream is an abstract class for Java input, with a role analogous to FilterOutputStream of output.

The following table summarizes the file input classes we will describe:

Summary of InputStream Hierarchy
Class/Interface Description
InputStream Generic byte input stream.
Reader Generic char input stream.
FileInputStream Basic byte input stream.
FileReader Basic char input stream.
FilterInputStream Allow byte streams to be chained.
FilterReader Allow char streams to be chained.
DataInputStream Read primitive data types in binary.
BufferedInputStream Buffer an InputStream.
BufferedReader Buffer a Reader.
StringBufferInputStream InputStream, but get input from string.

FileInputStream/FileReader

Use FileInputStream if you want to read bytes from a stream. For example,
// read a single byte followed by a byte array
// from a file called "junk"
byte[] buffer = new byte[1024];
FileInputStream f;
f = new FileInputStream("junk");
if ( f!=null )
{
   f.read();
   f.read(buffer); // reads up to 1024 bytes
   f.close(); // free up system resource
}
The different Reader classes work in the same manner but work on the character-level, instead of the byte level. If you are working with text, you should use the Reader objects, if you are using Java 1.1.

DataInputStream

If you want to read items of primitive type, portably stored in binary, use class DataInputStream. For example,
int i;
FileInputStream f = new FileInputStream("junk");
DataInputStream df = new DataInputStream(f);
i = df.readInt();
df.close(); // close and free up system resource.
f.close(); // close in reverse order

StringBufferInputStream

Use class StringBufferInputStream when you want to read input from a String rather than from the file system. For example,
// Read single byte followed by byte array from 
// string. The string has ASCII characters in it, 
// but the chars are read as binary bytes.
String junk = "abc34def";
byte[] buffer = new byte[1024];
StringBufferInputStream f =
      new StringBufferInputStream(junk);
byte b = f.read(); // read a byte
f.read(buffer);
// buffer will be {`b','c','3','4','d','e','f'}
f.close();

Random Access I/O

You often want to read or write data at random positions within a file, rather than sequentially as you would with a magnetic tape. The Java RandomAccessFile class behaves like a combined DataOutputStream and DataInputStream. RandomAccessFile implements both the DataOutput and DataInput interfaces. RandomAccessFile objects are created from a String filename or File object like other stream objects, but a mode constructor argument is also required. The mode is either String "r" (read only) or "rw" (read/write), just like fopen() in C or C++.

For example,

// open file junk for reading and writing
RandomAccessFile f = 
   new RandomAccessFile("junk", "rw");
// open crud for reading only
RandomAccessFile ff = 
   new RandomAccessFile("crud", "r");
To append information to a RandomAccessFile, seek to the end of the file:
f.seek(f.length());
The beginning of a file is considered position 0. Therefore, to seek to the beginning of a file, use:
f.seek(0);
The following example writes an integer and a floating point number to a file, rewinds the file, and reads them back:
RandomAccessFile f = 
   new RandomAccessFile("junk", "rw");
f.writeInt(34);
f.writeDouble(3.14159);
f.seek(0);
int i = f.readInt();
double d = f.readDouble();

Reading From Remote File Systems

You can do reads and writes for files on remote file systems by using sockets. Reads can also be done via URL objects. You can ask a URL object for an InputStream from which to read. For example,
try {
   InputStream in;
   byte[] buffer = new byte[100];
   URL url = new
      URL("http://www.MageLang.com/index.html");
   in = url.openStream();
   DataInputStream df = new DataInputStream(in);
   System.out.println(df.readLine());
   df.close();
}

catch (Exception e) {
   System.out.println(e.getMessage());
}

Magercises

1. Streams of primitive elements.
2. Text streams
3. Read a file on another machine.


Copyright © 1996-1997 MageLang Institute. All Rights Reserved.