File I/O

Topics

  1. Introduction
  2. Text Input
  3. Text Output
  4. Binary Input and Output

1. Introduction

Java® uses a stream-based approach to input and output. A stream in this context is a flow of data, which could either be read in from a data source (e.g. file, keyboard or socket) or written to a data sink (e.g file, screen, or socket). Java® currently supports two types of streams:

  • 8-bit streams. These are intended for binary data i.e. data that will be manipulated at the byte level. The abstract base classes for 8-bit streams are InputStream and OutputStream.
  • 16-bit streams. These are intended for character data. 16-bits streams are required becuase Java®'s internal representation for characters is the 16-bit Unicode format rather than the 8-bit ASCII format. The abstract base classes for 16-bit streams are Reader and Writer.

It is possible to create a 16-bit Reader from an 8-bit InputStream using the InputStreamReader class e.g.

Reader r = new InputStreamReader(System.in);      // System.in is an example of an InputStream.

Likewise, it is possible to create a 16-bit Writer from an 8-bit OutputStream using the OutputStreamWriter class e.g.

Writer w = new OutputStreamWriter(System.out);     // System.out is an example of an OutputStream.

2. Text Input

The FileReader class is used to read characters from a file. This class can only read one 16-bit Unicode character at a time (characters that are stored in 8-bit ASCII will be automatically promoted to Unicode.) In order to read a full line of text at once, we must layer a BufferedReader on top of the FileReader. Next, the individual words in the line of text can be extracted using a StringTokenizer. If the text contains numbers, we must also perform String to Number conversion operations, like Integer.parseInt() and Double.parseDouble().

import java.io.*;
import java.util.*;

 public class Main {
    public static void main(String[] args) {
        try {
            readText(args[0]);
        }
        catch (IOException e) {
            e.printStackTrace();
        }
    }

    // This function will read data from an ASCII text file.
    public static void readText(String fileName) throws IOException {
        // First create a FileReader.  A Reader is a 16-bit input stream,
        // which is intended for all forms of character (text) input.
        Reader reader = new FileReader(fileName);

        // Now create a BufferedReader from the Reader.  This allows us to
        // read in an entire line at a time.
        BufferedReader bufferedReader = new BufferedReader(reader);
        String nextLine;

        while ((nextLine = bufferedReader.readLine()) != null) {
            // Next, we create a StringTokenizer from the line we have just
            // read in.  This permits the extraction of nonspace characters.
            StringTokenizer tokenizer = new StringTokenizer(nextLine);

            // We can now extract various data types as follows.
            String companyName = tokenizer.nextToken();
            int numberShares = Integer.parseInt(tokenizer.nextToken());
            double sharePrice = Double.parseDouble(tokenizer.nextToken());

            // Print the data out on the screen.
            System.out.print(companyName + " has " + numberShares);
            System.out.println(" million shares valued at $" + sharePrice);

            // Close the file.
            bufferedReader.close();
        }
    }
}

This program can be easily converted to read in data from the keyboard. Simply replace

    Reader reader = new FileReader(fileName);

with

    Reader = new InputStreamReader(System.in);

3. Text Output

The FileWriter class is used to write text to a file. This class is only capable of writing out individual characters and strings. We can layer a PrintWriter on top of the FileWriter, so that we can write out numbers as well.

import java.io.*;
import java.util.*;
import java.text.*;

 public class Main {
    public static void main(String[] args) {
        try {
            writeText(args[0]);
        }
        catch (IOException e) {
            e.printStackTrace();
        }
    }

    // This function will write data to an ASCII text file.
    public static void writeText(String fileName) throws IOException {
        // First create a FileWriter.  A Writer is a 16-bit output stream,
        // which is intended for all forms of character (text) output.
        Writer writer = new FileWriter(fileName);

        // Next create a PrintWriter from the Writer.  This allows us to
        // print out other data types besides characters and Strings.
        PrintWriter printWriter = new PrintWriter(writer);

        // Now print out various data types.
        boolean b = true;
        int i = 20;
        double d = 1.124;
        String str = "This is some text.";

        printWriter.print(b);
        printWriter.print(i);
        printWriter.print(d);
        printWriter.println("\n" + str);

        // This is an example of formatted output.  In the format string,
        // 0 and # represent digits.  # means that the digit should not
        // be displayed if it is 0.
        DecimalFormat df = new DecimalFormat("#.000");
        printWriter.println(df.format(200.0));  // 200.000
        printWriter.println(df.format(0.123));  // .123

        // This will flush the PrintWriter's internal buffer, causing the
        // data to be actually written to file.
        printWriter.flush();

        // Finally, close the file.
        printWriter.close();
    }
}

4. Binary Input and Output

Binary input and output is done using the 8-bit streams. To read binary data from a file, we create a FileInputStream and then layer a DataInputStream on top of it. To write binary data to a file, we create a FileOutputStream and then layer a DataOutputStream on top of it. The following example illustrates this.

import java.io.*;

 public class Main {
    public static void main(String[] args) {
        try {
            writeBinary(args[0]);
            readBinary(args[0]);
        }
        catch (IOException e) {
            e.printStackTrace();
        }
    }

    // This function will write binary data to a file.
    public static void writeBinary(String fileName) throws IOException {
        // First create a FileOutputStream.
        OutputStream outputStream = new FileOutputStream(fileName);

        // Now layer a DataOutputStream on top of it.
        DataOutputStream dataOutputStream = new DataOutputStream(outputStream);

        // Now write out some data in binary format.  Strings are written out
        // in UTF format, which is a bridge between ASCII and Unicode.
        int i = 5;
        double d = 1.124;
        char c = 'z';
        String str = "Some text";

        dataOutputStream.writeInt(i);           // Increases file size by 4 bytes.
        dataOutputStream.writeDouble(d);   // Increases file size by 8 bytes.
        dataOutputStream.writeChar(c);      // Increases file size by 2 bytes.
        dataOutputStream.writeUTF(str);     // Increases file size by 2+9 bytes.

        // Close the file.
        dataOutputStream.close();
    }

    // This function will read binary data from a file.
    public static void readBinary(String fileName) throws IOException {
        // First create a FileInputStream.
        InputStream inputStream = new FileInputStream(fileName);

        // Now layer a DataInputStream on top of it.
        DataInputStream dataInputStream = new DataInputStream(inputStream);

        // Now read in data from the binary file.
        int i;
        double d;
        char c;
        String str;

        i = dataInputStream.readInt();
        d = dataInputStream.readDouble();
        c = dataInputStream.readChar();
        str = dataInputStream.readUTF();

        System.out.print("integer " + i + " double " + d);
        System.out.println(" char " + c + " String " + str);

        // Close the file.
        dataInputStream.close();
    }
}