Department of Computer Science and Engineering, IIT Kanpur

CS210: Data Structures

Dr. R. K. Ghosh


Home | Notice Board | Instructor | TAs | Students | Assignments | Schedule | Lectures | Resources

File I/O and Streams in Java

Streams are a big topic in Java and it is not the intention here to discuss them all. You will learn about them as you write bigger and more involved programs. This text is to cover just the very basics that lets us write files, read files and communicate with the user. In fact the System.out.println() statement we've been using all along is an implementation of Streams.

Interactively communicating with the user

A program that asks the user for his/her name and then prints a personalized greeting is provided below. 

import java.io.*;

class PersonalHello {

  public static void main (String args[])
    {

      byte name[] = new byte[100];
      int n_read = 0;

      System.out.println("What is your name?");
      try {
        n_read = System.in.read(name);
        System.out.print("Hello ");
        System.out.write(name,0,n_read);
      }
      catch (IOException e) {
        System.out.print("Sorry.  can't catch your name.");
      }

    }

}

In code that does any significant input or output we begin by importing all the various java.io classes. import.java.io.*; does this and is as common in Java applications as #include <stdio.h> in C programs.

Most of the reading and writing in Java will be done with bytes. Here we've started with an array of bytes that will hold the user's name.

First a query is printed requesting the user's name. Then the user's name is read using the System.in.read() method. This method takes a byte array as an argument, and places whatever the user types in that byte array. Then, "Hello" is printed using System.out.print("Hello "). Finally, the user's name is printed using System.out.write.

The program doesn't actually see what the user types until he or she types a carriage return. This gives the user the opportunity to erase mistakes by backspace and delete. Once the return key is pressed, everything in the line is copied into the array.

What happens if the user types more than 100 characters of text before hitting a carriage return? In many programming languages this would lead to a rather nasty program crash. It's also the sort of bug that often gets out the door in a shipping product since programmers often fail to test their programs against extreme inputs. However Java has been programmed a little more safely. System.in.read() won't read past the end of the array even though we didn't explicitly check to make sure the input was sufficiently small. It may possibly be due to the fact that the System.in.read() method internally checks the length of the array it's been passed using the name.length.

Reading Numbers

Often strings are not enough. Frequently, we may want to ask the user for a number as input. All user input comes in as strings so the input string should be converted into a number.

Let us write a getNextInteger() method that will accept an integer from the user. The program follows: 

  static int getNextInteger() {

    String line;

    DataInputStream in = new DataInputStream(System.in);
    try {
      line = in.readLine();
      int i = Integer.valueOf(line).intValue();
      return i;
    }
    catch (Exception e) {
      return -1;
    }

  } // getNextInteger ends here

Reading Formatted Data

Often we want to read not just one number but multiple numbers. Occasssionally, we may also need to read text and numbers on the same line. For this purpose Java provides the StreamTokenizer class.  We can define a class, say FormattedInput , which defines the methods to return particular type of data item retrieved from the standard input by the StringTokenizer object. In the class FormattedInput we don't require a constructor because the StringTokenizer object instance variable is initialized. We can therefore, straightway write the method to read a String or int or formatted input via the standard input. The FormattedInput class is provided below. 

import java.io.*;
public class FormattedInput {
  public String stringRead() {
    try {
      for (int i=0; i<5;i++) {
        int tokenType = tokenizer.nextToken();

        // If input is a string of alphabets then
        // accept it and return the string value.
        if (tokenType == tokenizer.TT_WORD || 
            tokenType=='\"')
           return tokenizer.sval;

        // If input is ! then return it to signal the
        // end of input.
        else if (tokenType=='!') return "!";
             else {
                // Strings with non-alphabets must be
                // specified within double quotes.
                System.out.println("Incorrect input. 
                                    Reenter a 0string
                                    between double
                                    qoutes");
                continue;
             }
       }

       // Upto five failures are tolerated during
       // input. 
       System.out.println("five failures reading a 
                           string. Program
                           terminated");
       System.exit(1);
       return null;
     } catch (IOException e) { 
           // Catch error generating IOException and 
           // indicate the error condition. 
           System.out.println(e);
           System.exit(1);
           return null;
       }

  }

  // Instance variable for the StreamTokenizer object
  // is initialized as a instance of input stream 
  // object.
  private StreamTokenizer tokenizer=new
            StreamTokenizer(
                    new InputStreamReader(System.in));
}

For reading an int value method inttRead() can be written similarly. The method is provided below without any further clarifications.
 

public int intRead() {
    try {
      for (int i=0; i<5;i++) {

        // If input is an integer of then
        // accept it and return the number.
        if (tokenizer.nextToken() ==
              tokenizer.TT_NUMBER) 
           return tokenizer.nval;
        else {    
           System.out.println("Incorrect input: " 
                              +tokenizer.sval+. 
                               " Re-enter an integer");
           continue;
        }
      }

       // Upto five failures are tolerated during
       // input. 
       System.out.println("five failures reading a 
                           string. Program
                           terminated");
       System.exit(1);
       return 0;
     } catch (IOException e) { 
           // Catch error generating IOException and 
           // indicate the error condition. 
           System.out.println(e);
           System.exit(1);
           return 0;
       }

  }
 


Writing a text file

Sometimes we want to save your output for future analysis rather than merely scrolling it across a screen. To do this we'll need to learn how to write data to a file. The Fahrenheit to Celsius conversion program written below outputs to a file:

// Write the Fahrenheit to Celsius table in a file

import java.io.*;

class FahrToCelsius  {

  public static void main (String args[]) {

    double fahr, celsius;
    double lower, upper, step;

    lower = 0.0;    // lower limit of temperature table
    upper = 300.0;  // upper limit of temperature table
    step  = 20.0;   // step size

    fahr = lower;

    try {

      FileOutputStream fout =  new FileOutputStream("test.out");

      // now to the FileOutputStream into a PrintStream
      PrintStream myOutput = new PrintStream(fout);

      while (fahr <= upper) {  // while loop begins here
        celsius = 5.0 * (fahr-32.0) / 9.0;
        myOutput.println(fahr + " " + celsius);
        fahr = fahr + step;
      } // while loop ends here

    }  // try ends here
    catch (IOException e) {
      System.out.println("Error: " + e);
      System.exit(1);
    }

  } // main ends here

}

There are only three things necessary to write formatted output to a file rather than to the standard output:

  1. Open a FileOutputStream using a line like

  2.      FileOutputStream fout =  new FileOutputStream("test.out");
         This line initializes the FileOutputStream with the name of the file you want to write into. 
  3. Convert the FileOutputStream into a PrintStream using a statement like

  4.      PrintStream myOutput = new PrintStream(fout);
         The PrintStream is passed the FileOutputStream from step 1. 
  5. Instead of using System.out.println() use myOutput.println().

  6.     System.out and myOutput are just different instances of the PrintStream class.
        For a different PrintStream the syntax remains the same but the name of the PrintStream is changed. 


Reading a text file

Now that we know how to write a text file, let's try reading one. The following code implements the Unix cat utility in Java. It accepts a series of file names on the command line and then prints those filenames to the standard output in the order they were listed.

// Imitate the Unix cat utility

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 {
    FileInputStream fin =  new FileInputStream(args[i]);

    // now turn the FileInputStream into a DataInputStream
    try {
      DataInputStream myInput = new DataInputStream(fin);

      try {
        while ((thisLine = myInput.readLine()) != null) {
          // while loop begins here
          System.out.println(thisLine);
        } // while loop ends here
      }
      catch (Exception e) {
       System.out.println("Error: " + e);
      }
    } // end try
    catch (Exception e) {
      System.out.println("Error: " + e);
    }

   } // end try
   catch (Exception e) {
    System.out.println("failed to open file " + args[i]);
    System.out.println("Error: " + e);
  }
  } // for end here

  } // main ends here

}


Home | Notice Board | Instructor | TAs | Students | Assignments | Schedule | Lectures | Resources