Wednesday, September 5, 2007

Read and write data with Java's I/O streams

All of Java's I/O is based upon streams. Once you learn the syntax for one stream; you can work with any other type in Java. This article introduces some ways of working with Java streams.

The Java input/output (I/O) facilities provide a simple, standardised API for reading and writing character and byte data from various data sources. In this article, we'll explore the I/O classes, interfaces, and operations provided by the Java platform. Let's start by taking a look at Java streams.

Streams

All of Java's I/O facilities are based on streams that represent flowing sequences of characters or bytes. Java's I/O streams provide standardised ways to read and write data. Any object representing a mutable data source in Java exposes methods for reading and writing its data as a stream.

Java.io is the main package for most stream-oriented I/O classes. This package presents two abstract classes, InputStream and OutputStream. All other stream-oriented I/O classes extend these base classes.

The java.io package exposes a number of classes and interfaces that provide useful abstractions on top of the character and byte reading and writing operations defined by InputStream and OutputStream. For example, the ObjectInputStream class provides methods that allow you to read data from a stream as a Java object, and the ObjectOutputStream provides methods that allow you to write data to a stream as a Java object.

Optimised reading and writing

JDK 1.1 added a collection of reader and writer classes that provide more useful abstractions and improved I/O performance than the existing streams classes. For instance, the BufferedReader and BufferedWriter classes are provided to read text from and write text to character-based input streams and output streams. The BufferedReader class buffers characters to more efficiently read characters, arrays, and lines. The BufferedWriter class buffers characters to more efficiently write characters, arrays, and strings. The size of the buffer used by the BufferedReader and BufferedWriter classes can be set as desired.

Reader and writer classes provided by the Java I/O Framework include the LineNumberReader class, the CharArrayReader class, the FileReader class, the FilterReader class, the PushbackReader class, the PipedReader class, and the StringReader class, among others. These classes are wrappers on top of the InputStream and OutputStream classes and thus provide methods that are similar to InputStream and OutputStream. However, these classes provide more efficient and useful abstractions for reading and writing specific objects, such as files, character arrays, and strings.

Reading data

An input stream is typically opened for you automatically when it is retrieved from the corresponding data source object or when you construct one of the reader objects. For example, to open the input stream for a file, we pass the name of the file into a java.io.FileReader object's constructor as follows:

java.io.FileReader fileReader = new java.io.FileReader("/home/me/myfile.txt");

To read the next available byte of data from a FileReader's underlying input stream, use the read method with no parameters. The snippet in Listing A reads text from a file, one character at a time, and writes it to System.out.

Listing A

int aChar = 0;
while ((aChar = fileReader.read()) >= 0)
{
System.out.print((char)aChar);
}

To read a given number of bytes from an input stream into a char array, use the read method with one char[] parameter. The length of the array is used to determine the number of characters to read. Listing B demonstrates this technique.

Listing B

char[] buffer = new char[256];
while ((fileReader.read(buffer)) >= 0)
{
System.out.print(new String(buffer));
}

To close an input stream and release any system resources used by the stream, use the close method as follows:

fileReader.close();

Writing data

Like an input stream, an output stream is typically opened for you automatically when it is retrieved from the corresponding data source object or when you construct one of the writer objects. For example, to open the output stream for a file, we pass the name of the file into a java.io.FileWriter object's constructor as follows:

java.io.FileWriter fileWriter = new java.io.FileWriter("/home/me/out.txt");

To write one specified character to an output stream, use the write method with one int parameter. The int parameter represents the character to write.

int aChar = (int)'X';

fileWriter.write(aChar);

To write a specific number of bytes from a specified char array starting at a given offset to an output stream, use the write method with a char[] parameter, an int offset parameter, and an int length parameter as shown in the following example:

fileWriter.write(buffer, 0, byteCount);

To close an output stream and release any system resources associated with the stream, use the close method, like this:

fileWriter.close();

To force any buffered data out of an output stream, use the flush method as follows:

fileWriter.flush();

Putting it all together

We can use what we have learned to read from one file and simultaneously write to another, as demonstrated in Listing C.

Listing C

try
{
java.io.FileReader fileReader =
new java.io.FileReader("/home/me/temp.txt");
java.io.FileWriter fileWriter =
new java.io.FileWriter("/home/me/out.txt");
char[] buffer = new char[256];
int byteCount = 0;
while ((byteCount = fileReader.read(buffer)) >= 0)
{
fileWriter.write(buffer, 0, byteCount);
}
fileWriter.flush();
fileWriter.close();
fileReader.close();
}
catch(Exception e)
{
System.out.println(e.toString());
}

Summary

The Java I/O facilities add a simple and standardised API for reading and writing character and byte data from various data sources. Experience obtained while working with Java streams for one type of data source can be carried over to any other type of data source exposed by Java.

No comments: