SUNY Geneseo, Department of Computer Science


Text Input and Output in Java

by Doug Baldwin

Input and output in Java are provided by a collection of library classes that ultimately provide an astonishly uniform interface to all sorts of input and output, from the simplest to the most sophisticated. A Java program can read a Web page from the other side of the world in much the same way it reads a simple string of text from the keyboard; it can send text to a network server in much the same way it sends text to a user at the computer's console. The drawback to this powerful I/O system is that simple I/O (particularly input) seems more complicated than necessary at first. This document introduces programmers to some of the concepts underlying input and output in Java, and to some simple methods for reading and writing text.

Basic Concepts

The basic objects that Java programmers use to do input and output are known as streams, readers, and writers. Most input or output operations can also generate exceptions, and so programmers need to know how to work with exceptions in order to do input or output. Finally, most I/O-related classes are in the "java.io" package in the Java libraries. Programs that do text input or output must therefore import this package.

Streams

A stream is a sequence of data, from which programs can input values or into which programs can output values. The metaphor is a stream of water flowing through a pipe -- by opening a tap in the pipe you can receive (input) water, or you can pump (output) your own water into the pipe. Someone else at another position along the pipe can open their own tap to receive water you have pumped into the pipe, or can pump water into the pipe for you to receive. In computer terms, the people on the metaphorical pipe could be two programs communicating over a network, a program and some files, a program and a user at a console, etc. The water in the pipe represents the data that is being communicated.

Streams come in two basic forms: input streams, from which a program can read data, and output streams, into which a program can write data.

Readers and Writers

Streams are designed to transmit arbitrary kinds of data -- anything from raw bits out of a computer's memory to entire objects can flow through streams. Text, however, is considerably less arbitrary than this. The data values in text are always characters, text has conventions that arbitrary data doesn't have (e.g., text can be broken into lines), etc. Thus, rather than input or output text directly through a stream, it is often more convenient to put an intermediary object between the program and the stream. This intermediary can always read or write characters (so the programmer needn't always say explicitly "this is a character"), it can understand the special significance of end-of-line characters in input or automatically insert them into output, etc. These intermediary objects are readers (for input) and writers (for output).

A program that does text input typically creates a reader object connected to the data source (the connection involves a stream object, which may be created implicitly when creating the reader, or may be explicit). Then the program sends messages to the reader to retrieve characters or lines through the reader. To do text output, a program creates a writer connected to the data sink (again, the connection involves a stream, but it may be implicit in the writer). Then the program sends messages to the writer that make it deliver characters or strings to the data sink.

The following diagram illustrates the relationships between streams, readers or writers, and the code you are likely to write as a programmer needing to do text input or output.

[Characters Flow To/From Programs Via Reader or Writer]

Exceptions

Exceptions in Java are signals that something unexpected has happened. Typically the unexpected event is an error of some sort. Exceptions generally cause the part of the program that caused the unexpected event to exit immediately. Java programmers often speak of this as "throwing" an exception. Some other part of the program should handle, or "catch," the exception. For a longer introduction to exceptions, see the Guide to Exceptions at http://cs.geneseo.edu/~baldwin/reference/java-exceptions.http.

Input and output are fruitful sources of exceptions, because so much can go wrong with the external data source or sink. For example, disks and file systems can be too full to accept more data, files may not exist, programs at the other end of a network connection may crash, etc. Thus many I/O messages may generate exceptions, and programs that do I/O should be written to handle those exceptions. See below for examples of how programs can do this.

Nearly all the exceptions that can be generated by text input and output are instances of subclasses of the library class IOException. Thus it is usually sufficient to handle IOExceptions when you write code that does text input or output.

Importing the I/O Classes

The classes of objects that do input or output all come from the Java class libraries. They are all in a package named "java.io", which is not automatically imported into Java programs. Thus you need to explicitly import it into any source files you write that do input or output. The easiest way to do this is to import the whole package, by putting the statement

    import java.io.*;

at the beginning of your source file(s).

Text Input

The general strategy for doing text input is to create a reader for the input source, and then send it read or readLine messages to input individual characters or lines. There are several kinds of reader that you might use, the most common being a class named BufferedReader.

Class BufferedReader

A BufferedReader is a kind of reader that groups, or "buffers" characters as it reads them from the underlying stream. Buffering makes reading more efficient, and allows BufferedReader objects to deliver whole lines of text as strings.

Creation

The most common constructor for BufferedReaders takes another (unbuffered) reader as its parameter. This constructor initializes a BufferedReader to read and buffer characters from the reader provided as a parameter. To kinds of reader commonly used as this parameter are FileReader (for reading from files) and InputStreamReader (for reading from arbitrary input streams). Both are discussed in more detail below. For example, here is how you might create a BufferedReader that reads from the standard keyboard input:

    InputStreamReader unbuffered = new InputStreamReader( System.in    );
    BufferedReader keyboard = new BufferedReader( unbuffered );

Input

Once you have created a BufferedReader, you can read either individual characters, or whole lines, from it.

To read a line of text, use the readLine message. This message has no parameters. It returns a string containing the next line from the BufferedReader. If there is no more unread text in the BufferedReader, readLine returns null. The readLine message may throw an IOException if it is unable to read for any reason other than being at the end of the reader. Here is an example of reading a line from the keyboard BufferedReader created above:

    try {
        String inputLine = keyboard.readLine();
        if ( inputLine != null ) {
            // ... process "inputLine" ...
        }
        else {
            System.err.println( "No more keyboard input." );
        }
    }
    catch ( IOException error ) {
        System.err.println( "Error reading keyboard: " + error );
    }

Note that this example declares the variable that receives the input line inside the try block. Thus that variable is only defined within the try block, and so all processing of the input has to happen within that block. This is only one of many ways of setting up the interaction between a readLine and the try that handles errors it might throw. You may combine the readLine and try in other ways in your own code when appropriate.

To read single characters from a BufferedReader, use the read message. This message has no parameters. It returns an integer, which contains the Unicode code for the next character from the BufferedReader. If there are no more unread characters in the BufferedReader, read returns -1. You can cast non-negative integers returned by read to type char for processing as characters. The read message may throw IOExceptions if it can't read for any reason other than being at the end of the input. For example, here is code that reads a single character from the keyboard BufferedReader created above:

    try {
        int input = keyboard.read();
        if ( input >= 0 ) {
            char c = (char) input;
            // ... process the input character in variable "c" ...
        }
        else {
            System.err.println( "No more keyboard input." );
        }
    }
    catch( IOException error ) {
        System.err.println( "Error reading keyboard: " + error );
    }

Closing a BufferedReader

When you are through using a BufferedReader, you can break your program's connection to the underlying stream and data source by sending the BufferedReader a close message. This message takes no parameters, and has no return value. It may, however, throw an IOException if it cannot close the BufferedReader for some reason. For example

    try {
        keyboard.close();
    }
    catch ( IOException error ) {
        System.err.println( "Couldn't close keyboard reader: " + error );
    }

Class InputStreamReader

As mentioned above, the constructor for BufferedReader needs another reader as its parameter. One of the reader classes commonly used for this purpose is InputStreamReader, which is a reader for an arbitrary input stream.

The simplest constructor for InputStreamReaders takes the stream you want to read as its only parameter.

Java automatically provides an input stream connected to the keyboard in variable System.in. Use this variable as a parameter to the InputStreamReader constructor in order to create a reader that reads text that the user types. For example

    InputStreamReader keyReader = new InputStreamReader( System.in    ); 

Class FileReader

Another reader class commonly used to create BufferedReaders is FileReader. This class represents readers connected to disk files. Creating a FileReader implicitly creates an input stream to the file.

The simplest constructor for FileReaders takes the name of the file, as a String, as its only parameter. This constructor throws an IOException if the file does not exist. For example, to create a FileReader connected to a file named "textfile.txt", you could write

    try {
        FileReader textReader = new FileReader( "textfile.txt" );
        // ... Read data from "textfile.txt" here ...
    }
    catch ( IOException error ) {
        System.err.println( "Error processing the text file: " + error );
    }

Text Output

The general strategy for outputting text is to create a kind of writer known as a PrintWriter connected to the place you want to send your output, and then to use print or println messages to output the text. The print and println messages are exactly the ones you use to print text to the console. In fact, System.out, the object that represents the console, is an instance of a class closely related to PrintWriter (class PrintStream).

Class PrintWriter

PrintWriter is the simplest way to output text. This class provides the standard print and println messages for printing textual representations of just about every data type in Java. PrintWriter objects also intercept any exceptions generated by the output operations and handle them internally, so that clients needn't worry about handling I/O exceptions.

The simplest constructor for PrintWriter simply takes another writer as its only parameter.

When you are through writing data to a PrintWriter, you should send it a close message. This message takes no parameters and has no return return value. It breaks the connection between a program and a PrintWriter, and makes sure that all data sent to the PrintWriter has actually been delivered to the data sink.

As an example, here is a fragment of code that creates a PrintWriter connected to file "textout.txt" and writes the message "Hello file" to it. This example uses a class named FileWriter (described next) to connect to the file; all exception handling in this example is there because the FileWriter constructor may throw exceptions.

    try {
        FileWriter rawOut = new FileWriter( "textout.txt" );
        PrintWriter out = new PrintWriter( rawOut );
        out.println( "Hello file" );
        out.close();
    }
    catch ( IOException error ) {
        System.err.println( "Error writing to output file: " + error );
    }

Class FileWriter

The easiest way to write text to a file is to create a FileWriter object connected to the file, then create a PrintWriter connected to the FileWriter. Like FileReader, FileWriter implicitly creates a stream connected to the file.

The simplest constructor for FileWriter objects takes the file name, in a String, as its only parameter. This constructor may throw an IOException if it cannot create a writer for the file. The PrintWriter example above includes code that creates a FileWriter connected to file "textout.txt."

A Complete Example

To demonstrate Java text I/O in a more realistic context, here is a program that builds a text file by copying text from the keyboard to the file. The program prompts the user for the file's name, then copies all subsequent keyboard input to the file. The program stops copying when it either detects the end of the keyboard input, or receives a line containing only a "." (some computing environments provide a way for users to signal the end of keyboard input, the "." convention provides a way to end the input in systems that don't).

    import java.io.*;

    class TextIOExample {
        public static void main( String args[] ) {

            try {

                BufferedReader in = new BufferedReader( new InputStreamReader( System.in ) );
                System.out.print( "Destination file name = " );
                String fileName = in.readLine();
                PrintWriter out = new PrintWriter( new FileWriter( fileName ) );

                String textLine = in.readLine();
                while ( textLine != null && ! textLine.equals(".") ) {
                    out.println( textLine );
                    textLine = in.readLine();
                }

                out.close();
                in.close();
            }
            catch ( IOException error ) {
                System.err.println( "Error making file:" );
                System.err.println( "\t" + error );
            }
        }
    }

Last Updated September 29, 2003