CSC 207 Grinnell College Spring, 2012
 
Algorithms and Object-Oriented Design
 

Stream Input

Summary

This reading introduces some basics of input in Java, including input from the keyboard and from files.

Acknowledgment

Much of this reading comes from Basic Textual Input and Output in Java by Samuel Rebelsky, as part of his 2005 Java Espresso project.

A Historical Perspective

In the early days of computing, programs were run only in batch mode. That is, you would start the program, wait for it to compute results (or crash), and then read the results (or error log). Initially, programmers coded any values the program would use directly into the program. Soon thereafter, however, they realized that it was far more general to place the input to a program (and the output from a program) in separate files.

With the advent of interactive computing systems, it became even more important for programs to be able to read input from a variety of sources and to send output to a variety of targets. In early interactive systems, most input was based on text that came from the keyboard and that went to a monitor or printer that showed only alphanumeric characters. Hence, the focus of early input and output commands was textual input and output.

Even with the creation of modern window-based systems, in which input can come from mice, touchscreens, and a variety of other devices and output can be graphical and auditory as well as textual, it is still useful to be able to read and write text, if only to save comments to a log and to gather data from a file.

Since text is the primitive form of information that comes from many devices, initial input is often text in many applications. When other types of input (e.g., integers or real numbers) are desired, input often involves two main steps:

Input Basics

In getting started with input in Java, it is useful to consider data from the terminal or from files as being a sequence of characters organized by lines. That is, input consists of a sequences of lines, and each line contains 0 or more characters. Such input is often called a stream. (As a parallel in Java, System.out is considered a stream for output.)

Since input streams are quite common for Java applications, it is hardly surprising that several pre-defined Java classes are available for handling input chores. For example, author, Mark Allen Weiss, suggests the use of a Scanner class; and instructor, Samuel Rebelsky, suggests the use of the BufferedReader and InputStreamReader classes. Work with these two approaches is largely parallel, as shown in the following notes.

Using the Scanner class Using BufferedReader and InputStreamReader
The Scanner class is discussed in Weiss's textbook and online in the Java 6 API The use of the BufferedReader and InputStreamReader classes is discussed in Java Espresso

To clarify how these classes might be used in an application, consider the file test.data that contains information on tests for a certain class. Each line of the file contains a student's first and last name and the results of three hour tests. Specifically, the first 19 characters in each line contain a name, the next 6 characters contain the first test score, the next 6 characters contain the second test score, and the remaining text on the line contains the third test score.

Our goal is to write a program that computes the average of the test scores for each student and the maximum and minimum scores for each test, and then prints the results in a nicely formatted table. For example, the table might have the following form:

     Name                        Test
First        Last        1       2       3     Average
Egbert       Bacon      88      85      92      88.33   
.
.
.
Maximum                 --      --      --
Minimum                 --      --      --

Reading Line-by-Line

As a first approach to this task, we use the built-in classes to read a line at a time. Then, given the line, we extract substrings for the name and the various test scores, we convert the test scores to integers for computation, and then we compute the averages and maximums and minimums.

Using the Scanner class Using BufferedReader and InputStreamReader
/* Program to compute test statistics for students
 */

package testScores;

import java.util.Scanner;
import java.io.FileReader;
import java.io.IOException;

public class TestScanner1
{
   public static void main (String args[])
   {   // identify name of file with test information
       String testFile = 
           "/home/walker/public_html/courses/207.sp13/readings/test.data";
        
       try  // try needed in case opening FileReader fails
       {
           // set up file reader for given data file
           FileReader fileReaderIn = new FileReader (testFile);
           // use FileReader object to initialize scanner
           Scanner fileIn = new Scanner (fileReaderIn);
             
           // declare variables for reading and for max and min
           int test1 = 0, test1max = 0, test1min = 0;
           int test2 = 0, test2max = 0, test2min = 0;
           int test3 = 0, test3max = 0, test3min = 0;
           boolean firstTime = true;
             
           //print headings
           System.out.println ("      Name                      Test");
           System.out.println ("First    Last\t\t 1 \t2 \t3      Average");  

           while (fileIn.hasNextLine())
           {
              // read entire line
              String line = fileIn.nextLine();
              // extract name field
              String name = line.substring (0, 18);
              // extract test 1 string and convert to integer
              //     extract character
              String intString = line.substring (19, 24);
              //     trim leading and trailing whitespace
              intString = intString.trim();
              //     convert to int by creating an Integer object
              test1 = new Integer(intString).intValue();

              // extract test 2 string and convert to integer
              intString = line.substring (25, 30);
              intString = intString.trim();
              test2 = new Integer(intString).intValue();
              // extract test 3 string and convert to integer
              intString = line.substring (31).trim();
              test3 = new Integer(intString).intValue();
              if (firstTime)
                 {   // the first data read become 
                     //  initial max and min values
                     test1max = test1min = test1;
                     test2max = test2min = test2;
                     test3max = test3min = test3;
                     firstTime = false;
                 }
              else
                 {
                     if (test1max < test1)
                         test1max = test1;
                     if (test1min > test1)
                         test1min = test1;
                     if (test2max < test2)
                         test2max = test2;
                     if (test2min > test2)
                         test2min = test2;
                     if (test3max < test3)
                         test3max = test3;
                     if (test3min > test3)
                         test3min = test3;
                 }
              // compute average and print results for line
              double avg = (test1 + test2 + test3) / 3.0;
              System.out.println (name + "\t" + test1 
                                       + "\t" + test2 
                                       + "\t" + test3
                                       + "\t" + avg);
           }
           // print concluding maximums and minimums
           System.out.println ("\nMaximum     \t\t" + test1max
                                  + "\t" + test2max
                                  + "\t" + test3max);
           System.out.println ("Minimum     \t\t" + test1min
                                  + "\t" + test2min
                                  + "\t" + test3min);
       }
       catch (IOException e)
           {   // identify any error that arises
               System.err.println (e);
           }
   }
}

/* Program to compute test statistics for students
 */

package testScores;

import java.io.File;
import java.io.FileReader;
import java.io.BufferedReader;
import java.io.IOException;

public class TestBufferedReader1
{
   public static void main (String args[])
   {   // identify name of file with test information
       String testFile = 
            "/home/walker/public_html/courses/207.sp13/readings/test.data";
        
       try  // try needed in case opening File fails
       {
           // set up file reader for given data file
           FileReader istream = new FileReader (testFile);
           // use FileReader object to initialize scanner
           BufferedReader fileIn = new BufferedReader (istream);
              
           // declare variables for reading and for max and min
           int test1 = 0, test1max = 0, test1min = 0;
           int test2 = 0, test2max = 0, test2min = 0;
           int test3 = 0, test3max = 0, test3min = 0;
           boolean firstTime = true;
              
           //print headings
           System.out.println ("      Name                      Test");
           System.out.println ("First    Last  \t\t 1\t 2\t 3      Average");   

           // read entire first line
           String line = fileIn.readLine();
           while (line != null)
           {
              // extract name field
              String name = line.substring (0, 18);
              // extract test 1 string and convert to integer
              //     extract character
              String intString = line.substring (19, 24);
              //     trim leading and trailing whitespace
              intString = intString.trim();
              //     convert to int by creating an Integer object
              test1 = new Integer(intString).intValue();

              // extract test 2 string and convert to integer
              intString = line.substring (25, 30);
              intString = intString.trim();
              test2 = new Integer(intString).intValue();
              // extract test 3 string and convert to integer
              intString = line.substring (31).trim();
              test3 = new Integer(intString).intValue();
              if (firstTime)
                 {   // the first data read become 
                     //  initial max and min values
                     test1max = test1min = test1;
                     test2max = test2min = test2;
                     test3max = test3min = test3;
                     firstTime = false;
                 }
              else
                  {
                      if (test1max < test1)
                          test1max = test1;
                      if (test1min > test1)
                          test1min = test1;
                      if (test2max < test2)
                          test2max = test2;
                      if (test2min > test2)
                          test2min = test2;
                      if (test3max < test3)
                          test3max = test3;
                      if (test3min > test3)
                          test3min = test3;
                  }
              // compute average and print results for line
              double avg = (test1 + test2 + test3) / 3.0;
              System.out.println (name + "\t" + test1 
                                       + "\t" + test2 
                                       + "\t" + test3
                                       + "\t" + avg);
              // read entire next line to prepare for next loop iteration
              line = fileIn.readLine();
           }
           // print concluding maximums and minimums
           System.out.println ("\nMaximum     \t\t" + test1max
                                  + "\t" + test2max
                                  + "\t" + test3max);
           System.out.println ("Minimum     \t\t" + test1min
                                  + "\t" + test2min
                                  + "\t" + test3min);

       }
       catch (IOException e)
           {   // identify any error that arises
               System.err.println (e);
           }
   }
}

Reading from the Keyboard

Instead of reading from a file, both approaches allow System.in to be used as an input stream in place of the FileReader in these examples. Also, since System.in is always opened when the program is opened, no try..catch block is needed when using the Scanner class.

After making the relevant changes, the first part of each program could be adjusted as follows:

Using the Scanner class Using BufferedReader and InputStreamReader
import java.util.Scanner;

public class TestScanner2
{
   public static void main (String args[])
   {       // use System.in to initialize scanner
           Scanner fileIn = new Scanner (System.in);
import java.io.InputStreamReader;
import java.io.BufferedReader;
import java.io.IOException;

public class TestBufferedReader2
{
   public static void main (String args[])
   {   
       try  // try needed in case opening the input stream fails
       {
           // create new input stream
           InputStreamReader istream = new InputStreamReader (System.in);
           // use FileReader object to initialize scanner
           BufferedReader fileIn = new BufferedReader (istream);

The full program for keyboard input with the Scanner class is available as TestScanner2.java The full program for keyboard input with the BufferedReader class is available as TestBufferedReader2.java

Note "End of Keyboard Input": When reading from the input in Eclipse in a Linux environment, use "control-d" to indicate the end of input (equivalent to an "end of file" when reading from a file.

Additional Capabilities for Reading

Both the Scanner and BufferedReader class have some additional capabilities, including the following.

Using the Scanner class Using BufferedReader and InputStreamReader
The Scanner class allows reading of several primitive types (e.g., int, double) as well as strings. the BufferedReader class allows rewinding of input, so parts of the same input can be read again.

The Scanner allows reading of various primitive types. Scanner divides the input into units or tokens. A program then can test the type of the next token and read the token as a given type. Some useful capabilities are:

  • hasNext() is another token available to read?
  • hasNextInt() is the next token an int?
  • hasNextDouble() is the next toke a double?
  • next() read the next token as a string
  • nextInt() read the next token as an int
  • nextDouble() read the next token as an double

Details are given in the Java documentation for the Scanner class

Rewinding Input (from Espresso: A Concentrated Introduction to Java by Samuel Rebelsky)

One nice part about BufferedReader objects is that they permit you to back up in the input file. As you will note from the documentation, you can use mark to indicate a place to which you will rewind and reset to rewind to that place. Note that mark takes one parameter, readAheadLimit, a number that indicates the maximum number of characters that will be read before the next call to reset. For example, here is some silly code that reads two lines from a file and then prepares to read the first of the two lines again.

Program TestScanner3.java shows how these Scanner methods might be used to read the file of names and test scores. Example:
fileIn.mark(1024);
System.out.println ("fileIn.readLine());
System.out.println ("fileIn.readLine());
fileIn.reset();

This document is available on the World Wide Web as

http://www.walker.cs.grinnell.edu/courses/207.sp13/readings/reading-input.shtml

created 2 May 2000 by Henry M. Walker
revised 27 January 2012
EOF for keyboard input added 12 February 2013
Valid HTML 4.01! Valid CSS!
For more information, please contact Henry M. Walker at walker@cs.grinnell.edu.


Copyright © 2011-2014 Henry M. Walker.
CC-BY-NC-SA
This work is licensed under a Creative Commons Attribution-Noncommercial-Share Alike 3.0 United States License .