Java I/O

Java I/O

Elliotte Rusty Harold

SD2000 West March 22, 2000

elharo@metalab.unc.edu

http://metalab.unc.edu/javafaq/


How I Got Interested in Java I/O

I really wanted to show people two things:

  1. How to read a line of text from the console

  2. How to do without printf()


The Main Theme:

Input and output in Java isn't weaker than input and output in C; just different.

How is it different?


What is a stream?


Where do streams come from


The Base Classes


The Stream Subclasses


Numeric data


The console

import java.io.*;


public class WriteHello {

  public static void main(String[] args) throws IOException {
  
    byte[] hello = {72, 101, 108, 108, 111, 32, 87, 
                    111, 114, 108, 100, 33, 10, 13};
    System.out.write(hello);

  }
  
}

Security Issues

I/O is extremely limited in applets:


Character data


The OutputStream class


Writing Bytes to Output Streams

import java.io.*;


public class HelloOutputStream {

  public static void main(String[] args) {

    String s = "Hello World\r\n";

    // Convert s to a byte array
    byte[] b = new byte[s.length()];
    s.getBytes(0, s.length()-1, b, 0);
    try {
      System.out.write(b);
      System.out.flush();
    }
    catch (IOException e) {
      System.err.println(e);
    }

  }

}

Flushing Output Streams


Closing Output Streams

The close() method closes the stream and releases any resources associated with the stream. Once the stream is closed attempts to write to it throw IOExceptions.


Subclassing OutputStream

package com.macfaq.io;

import java.io.*;

public class NullOutputStream extends OutputStream {

  public void write(int b) {
  
  }

  public void write(byte[] data) {
  
  }

  public void write(byte[] data, int offset, int length) {

  }

}

The InputStream Class

 public abstract int read() throws IOException
 public int read(byte[] data) throws IOException
 public int read(byte[] data, int offset, int length) throws IOException
 public long skip(long n) throws IOException
 public int available() throws IOException
 public void close() throws IOException
 public synchronized void mark(int readlimit)
 public synchronized void reset() throws IOException
 public boolean markSupported()
 

The read() method

 public abstract int read() throws IOException


Example of the read() method

import java.io.*;


public class Echo {

  public static void main(String[] args) {
  
    echo(System.in);
  
  }
  
  public static void echo(InputStream in) {
  
    try {
      while (true) {
        // Notice that although a byte is read, an int
        // with value between 0 and 255 is returned.
        // Then this is converted to an ISO Latin-1 char 
        // in the same range before being printed.   
        int i = in.read();
        // -1 is returned to indicate the end of stream
        if (i == -1) break;
        
        // without the cast a numeric string like "65"
        // would be printed instead of the character "A"
        char c = (char) i; 
        System.out.print(c);    
      }
    }
    catch (IOException e) {
      System.err.println(e); 
    }
    System.out.println();  
  
  }

}

Reading several bytes at once


Counting the available bytes

The available() method tests how many bytes are ready to be read from the stream without blocking.

public int available() throws IOException

import java.io.*;


public class EfficientEcho {

  public static void main(String[] args) {
  
    echo(System.in);
  
  }
  
  public static void echo(InputStream in) {
  
    try {
      while (true) {
        int n = in.available();
        if (n > 0) {
          byte[] b = new byte[n];
          int result = in.read(b);
          if (result == -1) break;
          String s = new String(b);
          System.out.print(s); 
        } // end if   
      } // end while
    } // end try
    catch (IOException e) {
      System.err.println(e); 
    }
  
  }

}


Skipping bytes


Marking and Resetting

 public synchronized void mark(int readlimit)
 public synchronized void reset() throws IOException
 public boolean markSupported()


Closing Input Streams

 public void close() throws IOException 


Subclassing InputStream


An InputStream subclass

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

public class RandomInputStream extends InputStream {

  private transient Random generator = new Random();

  public int read() {

    int result = generator.nextInt() % 256;
    if (result < 0) result = -result;
    return result;

  }

  public int read(byte[] data, int offset, int length) 
   throws IOException {

    byte[] temp = new byte[length];
    generator.nextBytes(temp);
    System.arraycopy(temp, 0, data, offset, length);
    return length;

  }

  public int read(byte[] data) throws IOException {

    generator.nextBytes(data);
    return data.length;

  }

  public long skip(long bytesToSkip) throws IOException {
  
    // It's all random so skipping has no effect
    return bytesToSkip;
  
  }
  
}


Writing Files

The java.io.FileOutputStream class represents an OutputStream that writes bytes to a file. It has the following public methods:

 public FileOutputStream(String name) throws IOException
 public FileOutputStream(String name, boolean append) throws IOException
 public FileOutputStream(File file) throws IOException
 public FileOutputStream(FileDescriptor fdObj)
 public native void write(int b) throws IOException
 public void write(byte[] data) throws IOException
 public void write(byte[] data, int offset, int length) throws IOException
 public native void close() throws IOException
 public final FileDescriptor getFD() throws IOException


Writing Files; An Example

This example reads user input from System.in and writes it into the files specified on the command line.

import java.io.*;


public class MultiType {

  public static void main(String[] args) {

    FileOutputStream[] fos = new FileOutputStream[args.length];

    for (int i = 0; i < args.length; i++) {
      try {
        fos[i] = new FileOutputStream(args[i]); 
      }
      catch (IOException e) {
        System.err.println(e); 
      }
    } // end for
    
    try {
       while (true) {
        int n = System.in.available();
        if (n > 0) {
          byte[] b = new byte[n];
          int result = System.in.read(b);
          if (result == -1) break;
          for (int i = 0; i < args.length; i++) {
            try {
              fos[i].write(b, 0, result); 
            }
            catch (IOException e) {
              System.err.println(e); 
            }
          } // end for
        } // end if   
      } // end while
    } // end try
    catch (IOException e) {
      System.err.println(e); 
    }

    for (int i = 0; i < args.length; i++) {
      try {
        fos[i].close(); 
       }
       catch (IOException e) {
         System.err.println(e); 
       }
    }


  } // end main
  
}

Appending Data to Files Streams

It's often useful to be able to append data to an existing file rather than overwriting it. To do this just pass the boolean value true as the second argument to the FileOutputStream() constructor. For example,

FileOutputStream fos = new FileOutputStream("16.html", true);

import java.io.*;


public class Append {

  public static void main(String[] args) {

    FileOutputStream[] fos = new FileOutputStream[args.length];

    for (int i = 0; i < args.length; i++) {
      try {
        fos[i] = new FileOutputStream(args[i], true); 
      }
      catch (IOException e) {
        System.err.println(e); 
      }
    } // end for
    
    try {
       while (true) {
        int n = System.in.available();
        if (n > 0) {
          byte[] b = new byte[n];
          int result = System.in.read(b);
          if (result == -1) break;
          for (int i = 0; i < args.length; i++) {
            try {
              fos[i].write(b, 0, result); 
            }
            catch (IOException e) {
              System.err.println(e); 
            }
          } // end for
        } // end if   
      } // end while
    } // end try
    catch (IOException e) {
      System.err.println(e); 
    }

    for (int i = 0; i < args.length; i++) {
      try {
        fos[i].close(); 
      }
      catch (IOException e) {
        System.err.println(e); 
      }
    } // end for


  } // end main
  
}

Reading Files

The FileInputStream class represents an InputStream that reads bytes from a file. It has the following public methods:

 public FileInputStream(String filename) throws FileNotFoundException
 public FileInputStream(File file) throws FileNotFoundException
 public FileInputStream(FileDescriptor fdObj)
 public native int read() throws IOException
 public int read(byte[] data) throws IOException
 public int read(byte[] data, int offset, int length) throws IOException
 public native long skip(long n) throws IOException
 public native int available() throws IOException
 public native void close() throws IOException
 public final FileDescriptor getFD() throws IOException
 

Filter Streams

The java.io.FilterInputStream and java.io.FilterOutputStream classes are concrete subclasses of InputStream and OutputStream that somehow modify data read from an underlying stream. You rarely use these classes directly, but their subclasses are extremely important, especially DataInputStream and DataOutputStream.

You connect filter streams to an underlying stream that supplies the actual bytes of data by passing the original stream to the filter stream's constructor. For example, to create a new DataOutputStream from a FileOutputStream you might do this:

FileOutputStream fos = new FileOutputStream("ln.txt");
DataOutputStream dos = new DataOutputStream(fos);

It's not uncommon to combine these into one line like this:

DataOutputStream dos = new DataOutputStream(new FileOutputStream("ln.txt"));


The Filter Stream Classes


Buffered Streams


PushbackInputStream


PrintStream

 public void print(boolean b)
 public void print(int i)
 public void print(long l)
 public void print(float f)
 public void print(double d)
 public void print(char s[])
 public void print(String s)
 public void print(Object obj)
 public void println()
 public void println(boolean x)
 public void println(char x)
 public void println(int x)
 public void println(long x)
 public void println(float x)
 public void println(double x)
 public void println(char x[])
 public void println(String x)
 public void println(Object x)


PrintStream is Evil


Data Streams


DataOutputStream

DataOutputStream declares these methods:

 public DataOutputStream(OutputStream out)
 public synchronized void write(int b) throws IOException
 public synchronized void write(byte[] data, int offset, int length) 
  throws IOException
 public final void writeBoolean(boolean b) throws IOException
 public final void writeByte(int b) throws IOException
 public final void writeShort(int s) throws IOException
 public final void writeChar(int c) throws IOException
 public final void writeInt(int i) throws IOException
 public final void writeFloat(float f) throws IOException
 public final void writeDouble(double d) throws IOException
 public final void writeBytes(String s) throws IOException
 public final void writeChars(String s) throws IOException
 public final void writeUTF(String s) throws IOException
 public final int size()
 public void flush() throws IOException


DataInputStream

DataInputStream has these methods:

 public DataInputStream(InputStream in)
 
 public final int read(byte[] input) throws IOException
 public final int read(byte[] input, int offset, int length) throws IOException
 
 public final void readFully(byte[] input) throws IOException
 public final void readFully(byte[] input, int offset, int length) throws IOException
  
 public final int skipBytes(int n) throws IOException
 public final boolean readBoolean() throws IOException
 public final byte readByte() throws IOException
 public final int readUnsignedByte() throws IOException
 public final short readShort() throws IOException
 public final int readUnsignedShort() throws IOException
 public final char readChar() throws IOException
 public final int readInt() throws IOException
 public final long readLong() throws IOException
 public final float readFloat() throws IOException
 public final double readDouble() throws IOException
 public final String readUTF() throws IOException
 public static final String readUTF(DataInput in) throws IOException


readLine() is Evil!

 public final String readLine() throws IOException


GZIPOutputStream

import java.io.*;
import java.util.zip.*;
import com.macfaq.io.*;


public class GZipper {

  public final static String GZIP_SUFFIX = ".gz";

  public static void main(String[] args) {

    for (int i = 0; i < args.length; i++) {
      try {
        FileInputStream in = new FileInputStream(args[i]);      
        FileOutputStream fout = new FileOutputStream(args[i] + GZIP_SUFFIX);
        GZIPOutputStream out = new GZIPOutputStream(fout);
        byte[] buffer = new byte[256];
        while (true) {
          int bytesRead = in.read(buffer);
          if (bytesRead == -1) break;
          out.write(buffer, 0, bytesRead);
        }        
        out.close();
      }
      catch (IOException e) {
        System.err.println(e);     
      }
    }

  }

}


GZIPInputStream

import java.io.*;
import java.util.zip.*;
import com.macfaq.io.*;


public class GUnzipper {

  public static void main(String[] args) {

    for (int i = 0; i < args.length; i++) {
      if (args[i].toLowerCase().endsWith(GZipper.GZIP_SUFFIX)) {
        try {
          FileInputStream fin = new FileInputStream(args[i]);      
          GZIPInputStream in = new GZIPInputStream(fin);
          FileOutputStream out = new FileOutputStream(
           args[i].substring(0, args[i].length()-3));
          byte[] buffer = new byte[256];
          while (true) {
            int bytesRead = in.read(buffer);
            if (bytesRead == -1) break;
            out.write(buffer, 0, bytesRead);
          }        
          out.close();
        }
        catch (IOException e) {
          System.err.println(e);     
        }
      }
      else {
        System.err.println(args[i] + " does not appear to be a gzipped file.");
      }
    }

  }

}


Subclassing FilterOutputStream

package com.macfaq.io;

import java.io.*;


public class TeeOutputStream extends FilterOutputStream {

  private OutputStream out1;
  private OutputStream out2;

  public TeeOutputStream(OutputStream stream1, OutputStream stream2) {
    super(stream1);
    out1 = stream1;
    out2 = stream2;
  }

  public synchronized void write(int b) throws IOException {
    out1.write(b);
    out2.write(b);  
  }

  public synchronized void write(byte[] data, int offset, int length) 
   throws IOException {
    out1.write(data, offset, length);
    out2.write(data, offset, length);
  }

  public void flush() throws IOException {
    out1.flush();
    out2.flush();  
  }
  
  public void close() throws IOException {
    out1.close();
    out2.close();
  }

}


Subclassing FilterInputStream

package com.macfaq.io;

import java.io.*;


public class PrintableInputStream extends FilterInputStream {

  public PrintableInputStream(InputStream in) {
    super(in);
  }

  public int read() throws IOException {
  
    int b = in.read();
    // printing, ASCII characters
    if (b >= 32 && b <= 126) return b;
    // carriage return, linefeed, tab, and end of file
    else if (b == 10 || b == 13 || b == 9 || b == -1) return b;
    // non-printing characters
    else return '?'; 

  }

  public int read(byte[] data, int offset, int length) throws IOException {
  
    int result = in.read(data, offset, length);
    for (int i = offset; i < offset+result; i++) {
      // do nothing with the printing characters
      // carriage return, linefeed, tab, and end of file
      if (data[i] == 10 || data[i] == 13 || data[i] == 9 || data[i] == -1) ;
      // non-printing characters
      else if (data[i] < 32 || data[i] > 126) data[i] = (byte) '?';
    }
    return result;
    
  }

}


Character Sets


Readers and Writers

The java.io.Reader and java.io.Writer classes are abstract superclasses for classes that read and write character based data. The subclasses are notable for handling the conversion between different character sets.

public abstract class Reader extends Object
public abstract class Writer extends Object


The Writer class


OutputStreamWriter


The Reader class


InputStreamReader


Other Readers and Writers


PrintWriter

 public PrintWriter(Writer out)
 public PrintWriter(Writer out, boolean autoFlush)
 public PrintWriter(OutputStream out)
 public PrintWriter(OutputStream out, boolean autoFlush)
 public void flush()
 public void close()
 public boolean checkError()
 protected void setError()
 public void write(int c)
 public void write(char buf[], int offset, int length)
 public void write(char buf[])
 public void write(String s,
 public void write(String s)
 public void print(boolean b)
 public void print(char c)
 public void print(int i)
 public void print(long l)
 public void print(float f)
 public void print(double d)
 public void print(char s[])
 public void print(String s)
 public void print(Object obj)
 public void println()
 public void println(boolean x)
 public void println(char x)
 public void println(int x)
 public void println(long x)
 public void println(float x)
 public void println(double x)
 public void println(char x[])
 public void println(String x)
 public void println(Object x)


BufferedReader


BufferedReader readLine() example

// Implement the Unix cat utility in java

import java.io.*;

class cat  {

  public static void main (String args[]) {
  
    String thisLine;
 
   //Loop across the arguments
   for (int i=0; i < args.length; i++) {
 
     //Open the file for reading
     try {
       BufferedReader br = new BufferedReader(new FileReader(args[i]));
       while ((thisLine = br.readLine()) != null) { // while loop begins here
         System.out.println(thisLine);
       } // end while 
     } // end try
     catch (IOException e) {
       System.err.println("Error: " + e);
     }
  } // end for
  
} // end main

}

Filter Readers and Writers


Formatted I/O


The Old Way

Input

Output


The New Way


Locales


Number Format

NumberFormat myFormat = NumberFormat.getInstance();
NumberFormat canadaFormat = NumberFormat.getInstance(Locale.CANADA);
Locale turkey = new Locale("tr", "");
NumberFormat turkishFormat = NumberFormat.getInstance(turkey);
Locale swissItalian = new Locale("it", "CH");
NumberFormat swissItalianFormat = NumberFormat.getInstance(swissItalian);


Formatting Numbers


Number Format example


import java.text.*;


public class FormatTest {

  public static void main(String[] args) {

    NumberFormat nf = NumberFormat.getInstance();
    for (double x = Math.PI; x < 100000; x *= 10) {
      String formattedNumber = nf.format(x);
      System.out.println(formattedNumber + "\t" + x);
    }

  }

}

U.S. English system results:
3.141		3.14159265358979
31.415		31.4159265358979
314.159		314.159265358979
3,141.592		3141.5926535897897
31,415.926	31415.926535897896

French results:
3,141		3.14159265358979
31,415		31.4159265358979
314,159		314.159265358979
3 141,592		3141.5926535897897
31 415,926	31415.926535897896

Specifying Precision

You specify the minimum and maximum of each type you want in each number using these four methods:

public void setMaximumIntegerDigits(int newValue)
public void setMinimumIntegerDigits(int newValue)
public void setMaximumFractionDigits(int newValue)
public void setMinimumFractionDigits(int newValue)

For example, to specify that myFormat should format numbers with at least 10 digits before the decimal point and at most 3 digits after, you would type:

myFormat.setMinimumIntegerDigits(10);
myFormat.setMaximumFractionDigits(3);


Precision Example

import java.text.*;


public class PrettyTable {

  public static void main(String[] args) {
  
    System.out.println("Degrees Radians Grads");
    NumberFormat myFormat = NumberFormat.getInstance();
    myFormat.setMinimumIntegerDigits(3);
    myFormat.setMaximumFractionDigits(2);
    myFormat.setMinimumFractionDigits(2);
    for (double degrees = 0.0; degrees < 360.0; degrees++) {
      String radianString = myFormat.format(Math.PI * degrees / 180.0);
      String gradString = myFormat.format(400 * degrees / 360);
      String degreeString = myFormat.format(degrees);
      System.out.println(degreeString + "  " + radianString 
       + "  " + gradString);
    }
    
  }

}


Output:

300.00  005.23  333.33
301.00  005.25  334.44
302.00  005.27  335.55
303.00  005.28  336.66
304.00  005.30  337.77
305.00  005.32  338.88
306.00  005.34  340.00
307.00  005.35  341.11
308.00  005.37  342.22
309.00  005.39  343.33
310.00  005.41  344.44
311.00  005.42  345.55
312.00  005.44  346.66
313.00  005.46  347.77
314.00  005.48  348.88


How big is 299792500?


Grouping


Currency Formats

If you know you're going to be working with money, you can request a currency formatter with the static NumberFormat.getCurrencyInstance() method:

public static final NumberFormat getCurrencyInstance()
public static NumberFormat getCurrencyInstance(Locale inLocale)

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

public class MinimumWage {

  public static void main(String[] args) {
  
    NumberFormat dollarFormat = NumberFormat.getCurrencyInstance(Locale.ENGLISH);
    double minimumWage = 5.15;
    
    System.out.println("The minimum wage is " 
     + dollarFormat.format(minimumWage));
    System.out.println("A worker earning minimum wage and working for forty");
    System.out.println("hours a week, 52 weeks a year, would earn " 
     + dollarFormat.format(40*52*minimumWage));
    
  }

}


Output:
This program prints
The minimum wage is $5.15
A worker earning minimum wage and working for forty 
hours a week, 52 weeks a year, would earn $10,712.00


Percent Formats


Parsing Input


CS 101 Homework

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


public class RootFinder {

  public static void main(String[] args) {
  
    Number input = null;
    
    try {
      BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
      NumberFormat nf = NumberFormat.getInstance();
      while (true) {
        System.out.println("Enter a number (-1 to quit): ");
        String s = br.readLine();
        try {
          input = nf.parse(s);
        }
        catch (ParseException e) {
          System.out.println(s + " is not a number I understand.");
          continue;
        }
        double d = input.doubleValue();
        if (d < 0) break;
        double root = Math.sqrt(d);
        System.out.println("The square root of " + s + " is " + root);
      }
    }
    catch (IOException e) {
      System.err.println(e);  
    }
    
  }

}


Output:
% java RootFinder
Enter a number (-1 to quit): 
87
The square root of 87 is 9.327379053088816
Enter a number (-1 to quit): 
65.4
The square root of 65.4 is 8.087026647662292
Enter a number (-1 to quit): 
3.151592
The square root of 3.151592 is 1.7752723734683644
Enter a number (-1 to quit): 
2,345,678
The square root of 2,345,678 is 1531.5606419596973
Enter a number (-1 to quit): 
2.998E+8
The square root of 2.998E+8 is 1.7314733610425546
Enter a number (-1 to quit): 
299800000
The square root of 299800000 is 17314.733610425545
Enter a number (-1 to quit): 
0.0
The square root of 0.0 is 0.0
Enter a number (-1 to quit): 
four
four is not a number I understand.
Enter a number (-1 to quit): 
4
The square root of 4 is 2.0
Enter a number (-1 to quit): 
Enter a number (-1 to quit): 
(12)
(12) is not a number I understand.
-1


Exponentials


Streams are not Thread Safe


Future Improvements and Additions


To Learn More



Questions?


Copyright 2000 Elliotte Rusty Harold
elharo@metalab.unc.edu
Last Modified March 17, 2000