Java 101: The ins and outs of standard input/output

how-to
Mar 02, 200116 mins
Core JavaDevelopment ToolsJava SE

Learn the basics of standard input, standard output, and standard error

In prior Java 101 articles, I referred to the concepts of redirection, standard input device, and standard output device. To demonstrate inputting data, several examples called System.in.read(). It turns out that System.in.read() inputs data from the standard input device. To demonstrate outputting data, examples called System.out.print() and System.out.println(). In contrast to System.in.read(), those methods — named sequences of executable code (to be explored in next month’s article) — send their output to the standard output device. Want to know more about standard I/O concepts? Read on!

Standard I/O is a standardized input/output mechanism that originates from the Unix operating system. Although this mechanism is mostly used with older non-GUI operating systems, standard I/O still plays a role in modern GUI (graphical user interface) operating systems, where people use it to debug malfunctioning programs and to teach input/output in entry-level programming courses.

As you’ve probably guessed, standard I/O uses devices for inputting and outputting data. These devices include standard input, standard output, and standard error.

Standard input

The standard input device is that part of the operating system that controls from where a program receives its input. By default, the standard input device reads that input from a device driver attached to the keyboard. However, you can redirect, or switch, the input source to a device driver attached to a file so that input seems to be “magically” coming from a file — instead of the keyboard.

A program inputs its data from the standard input device by calling Java’s System.in.read() method. Look in the SDK documentation and you’ll discover a class called System. That class contains a variable called in — an object created from a subclass of InputStream. The period character after System states that in belongs to System, and the period character after in states that read() belongs to in. In other words, read() is a method that belongs to an object called in, which in turn belongs to a class called System. (I will discuss more about classes, objects, and “belonging to” next month.)

System.in.read() takes no arguments and returns an integer, which has led some to believe that System.in.read() returns user-entered integer numbers. To clarify, System.in.read() either returns a key’s 7-bit ASCII code (if the standard input device is set to the keyboard) or an 8-bit byte from a file (if the standard input device has been redirected from the keyboard to a file). In either case, System.in.read() converts the code to a 32-bit integer and returns the result.

Assume that the standard input device is set to the keyboard. The following is a description of what happens under Windows: When you type a key on a Windows-controlled keyboard, the operating system stores that key’s 7-bit ASCII code in an internal key buffer. That key buffer holds up to roughly 16 ASCII codes and is organized as a first-in/first-out circular queue data structure. System.in.read() retrieves the ASCII code from the head of the key buffer and then removes that code from the key buffer. That 7-bit ASCII code then converts to an int — by System.in.read() prepending 25 zero bits to the code — and returns to the method’s caller. A second System.in.read() method call retrieves the next ASCII code, which is now at the head of the key buffer, and so on.

Suppose there are no ASCII codes in the key buffer. What happens? System.in.read() waits for the user to type keys and press the terminator. Under Windows, that terminator is the Enter key. Pressing Enter causes Windows to store a carriage return code (ASCII 13) followed by a new-line code (ASCII 10) in the key buffer. Therefore, the key buffer might contain several ASCII codes followed by a carriage return and a new-line character. The first of those codes returns from System.in.read(). Check out that activity by keying in, compiling, and running the Echo application; its source code appears in Listing 1.

Listing 1. Echo.java

// Echo.java
class Echo
{
   public static void main (String [] args) throws java.io.IOException
   {
      int ch;
      System.out.print ("Enter some text: ");
      while ((ch = System.in.read ()) != 'n')
         System.out.print ((char) ch);
   }
}

Echo completes the following steps:

  1. Calls the System.out.print() method, which takes a String argument, to output a prompt
  2. Calls System.in.read() to input ASCII codes from the standard input device as 32-bit integers
  3. Converts those 32-bit integers to 16-bit Unicode characters by way of the (char) cast
  4. Calls the System.out.print() method, which takes a char argument, to echo those Unicode characters to the standard output device

The last three steps in the previous four steps take place in a while loop, and continue until a new-line character is read. To run Echo so that it inputs from the keyboard and outputs to the screen, issue the following command line: java Echo.

Although System.in.read() never throws an exception (see the word-counting topic in this article for a definition of that term), when the standard input device is set to the keyboard, it might throw an exception when you redirect the standard input device from the keyboard to a file. For example, suppose you redirect the standard input device to a file, and System.in.read() reads content from the file. Now suppose that the file is situated on a floppy disk, and the user ejects that disk during the read operation. When the ejection takes place, System.in.read() throws an exception, informing the program that it can’t read the file. That provides the reason for appending the throws java.io.IOException clause to the main() method header. (You will explore exceptions, throwing exceptions, and related concepts in a future article.)

How do you redirect the standard input device so that input originates from a file? The answer is to introduce a less-than sign, <, on the command line and follow that symbol with a filename. To see how that works, issue the following command line: java Echo <Echo.java. The command line redirects the standard input device to a file called Echo.java. When Echo runs, because each line ends in a new-line character, only the first line of text in Echo.java appears on the screen.

Suppose you need a utility program that reads an entire file and either displays the file’s contents on the screen, copies those contents to another file, or copies those contents to a printer. Unfortunately, the Echo program only performs that task until it encounters the first new-line character. What do you do? The answer to the problem lies in the Type application. Listing 2 provides the source code:

Listing 2. Type.java

// Type.java
class Type
{
   public static void main (String [] args) throws java.io.IOException
   {
      int ch;
      while ((ch = System.in.read ()) != -1)
         System.out.print ((char) ch);
   }
}

Type resembles Echo, however, there is no prompt, and the while loop tests against -1 (which indicates end of file) instead of n (which indicates end of line). To run Type, issue the following command line: java Type <Type.java. The contents of Type.java — or whatever file is specified — will display. As an experiment, try specifying java Type. What do you think will happen? (Hint: this program resembles Echo but doesn’t end until you press Ctrl+C.)

Earlier, I mentioned that some programmers mistakenly think that System.in.read() returns a user-entered number. As you’ve just seen, that isn’t the case. But what must you do if you want to use System.in.read() to retrieve a number? Take a look at the Convert application, whose source code is presented in Listing 3.

Listing 3. Convert.java

// Convert.java
class Convert
{
   public static void main (String [] args) throws java.io.IOException
   {
      System.out.print ("Please enter a number: ");
      int num = 0;
      int ch;
      while ((ch = System.in.read ()) != 'n')
         if (ch >= '0' && ch <= '9')
         {
             num *= 10;
             num += ch - '0';
         }
         else
             break;
      System.out.println ("num = " + num);
      System.out.println ("num squared = " + num * num);
   }
}

Listing 3’s Convert program prompts the user to enter a number (via System.out.print ("Please enter a number: ");). It reads these digits — one at a time — and converts each digit’s numeric code to a binary number that is added to a variable called num. Finally, calls to System.out.println() output the value inside num and the square of that value to the standard output device.

Convert demonstrates the time-honored technique of using a while loop to test for a digit, premultiplying a variable by 10 (to make room for the incoming digit), converting a digit to its binary equivalent, and adding that binary equivalent to the variable. However, that technique is not a sound technique to use if you’re writing a program for deployment in different countries as some countries use digits other than 0 through 9 — such as Tamil digits. To make the program operate with other digits, you need to expand the if statement to test for those digits and modify the ch - '0' expression. Fortunately, Java simplifies that task by providing a Character class, which you’ll explore in a future article.

Standard output

The standard output device is that part of the operating system that controls where a program sends its output. By default, the standard output device sends the output to a device driver attached to the screen. However, the output destination can be redirected to a device driver attached to a file or printer, which results in the same program displaying its findings on the screen, saving them in a file, or providing a hardcopy listing of the results.

You achieve standard output by calling Java’s System.out.print() and System.out.println() methods. Except for the fact that print() methods don’t output a new-line character after the data, the two method groups are equivalent. Methods exist to output Boolean, character, character array, double-precision floating-point, floating-point, integer, long integer, string, and object values. To demonstrate these methods, Listing 4 presents source code to the Print application.

Listing 4. Print.java

// Print.java
class Print
{
   public static void main (String [] args)
   {
      boolean b = true;
      System.out.println (b);
      char c = 'A';
      System.out.println (c);
      char [] carray = { 'A', 'B', 'C' };
      System.out.println (carray);
      double d = 3.5;
      System.out.println (d);
      float f = -9.3f;
      System.out.println (f);
      int i = 'X';
      System.out.println (i);
      long l = 9000000;
      System.out.println (l);
      String s = "abc";
      System.out.println (s);
      System.out.println (new Print ());
   }
}

Listing 4 has probably triggered some questions for you. First, what is all that System.out. business doing in front of println()? Again, refer to the System class in the SDK documentation. The class contains a variable called out — an object created from a class called PrintStream. The period character after System indicates that out belongs to System. The period character after out states that println() belongs to out. In other words, println() is a method that belongs to an object called out, which in turn belongs to a class called System.

The second question you might be asking yourself involves println() argument data types: how is it possible for the same println() method to be called with different types of argument data? The answer: because there are several println() methods in the PrintStream class. At runtime, the JVM knows which println() method to call by examining the number of method-call arguments and their data types. (Declaring several methods with the same name but different numbers of arguments and argument data types is known as method overloading. I will discuss that concept next month.)

Finally, you might be wondering about System.out.println (new Print ());. That method call illustrates the println() method, which takes an Object argument. First, the creation operator new creates an object from the Print class and returns a reference to — also known as the address of — that object. Finally, that address passes as an argument to the println() method, which takes an Object argument. The method converts the object’s contents to a string and outputs that string. By default, the string consists of the name of the object’s class, followed by an @ (at) character, followed by a hexadecimal-formatted integer that represents the object’s hashcode. (I will present hashcodes and the conversion of objects to strings in an upcoming article.)

Compile Print.java and run the program by issuing the following command line: java Print. You should see nine lines of output. Redirect that output to the out.dat file by issuing the following command line: java Print >out.dat. You can now view the contents of the file.

The greater-than sign, >, indicates standard output redirection. Whenever you want to redirect the standard output device from the screen to a file or printer, specify that symbol followed by the file or printer name on the command line. For example, redirect Print‘s output to a Windows printer by issuing the following command line: java Print >prn.

The PrintStream class provides a println() method, which takes no arguments. That method outputs a single new-line character. Behind the scenes, the various println() methods call their print() method counterparts followed by the no-argument println() method. A close look at the documentation for the no-argument println() method shows the new-line character being obtained from a system property called line.separator. That system property’s value is not necessarily the same as n. (I will discuss system properties in a future article.) To seek true platform independence, don’t hard-code n in your print() or println() arguments. (In this course, the n literal is often specified in source code.)

Example:

// The following method call is platform-dependent because of the n character.
System.out.print ("ABCnDEF");
// The following method calls are platform-independent.
System.out.println ("ABC");
System.out.print ("DEF");

Standard error

The standard error device is that part of the operating system that controls where a program sends error messages. Unlike the standard output device, you cannot redirect standard error on platforms such as Windows 98 SE. Use System.err instead of System.out to distinguish standard error from standard output:

Example:

// Output the following error message to standard error.
System.err.println ("usage: java copy srcfile dstfile");

It’s a good idea to use System.err instead of System.out for outputting error messages. After all, a user might become confused if a problem occurs in a program and no error message appears on the screen because the message is sent to the standard output device — which has been redirected from the screen to a file.

You might wonder why these introductory Java 101 articles don’t use GUIs to solicit input and display output. The reason is because Java’s GUI framework is more complex than standard I/O, and a complete discussion of this framework reaches far beyond the scope of introductory Java 101 articles. However, several articles from now, you will explore this GUI framework, and your reliance on standard I/O for a program’s input and output tasks will diminish.

Word counting

Now that you’ve explored the concepts of standard input, standard output, and standard error (as well as several simple programs and code fragments that demonstrate these concepts), let’s create a useful program that exploits these concepts.

Suppose you want to know how many words an article that you wrote contains. Instead of counting by hand, implement a utility program that counts words for you. Before writing the utility, you need to decide what constitutes a word. After all, it is pointless to write a word-counting program if you don’t know how to tell the program how to interpret a word. My definition of a word is a sequence of letters — either uppercase or lowercase. You could change that definition to include hyphen characters, if desired.

Listing 5 presents the source code to WordCount — a word-counting program that I’ve put together. Like myself, you might find this program to be useful.

Listing 5. WordCount.java

// WordCount.java
import java.io.*;
class WordCount
{
   static int nWords;
   public static void main (String [] args) throws IOException
   {
      int ch;
      // Read each character from standard input until a letter
      // is read.  This letter indicates the start of a word.
      while ((ch = System.in.read ()) != -1)
      {
         // If character is a letter then start of word detected.
         if (ch >= 'A' && ch <= 'Z' || ch >= 'a' && ch <= 'z')
         {
             do
             {
                ch = System.in.read ();
                if (ch >= 'A' && ch <= 'Z' ||
                    ch >= 'a' && ch <= 'z' ||
                    ch >= '0' && ch <= '9')
                    continue;
                else
                    break;
             }
             while (true);
             nWords++;
         }
      }
      System.out.println ("nTotal words = " + nWords);
   }
}

WordCount is pretty straightforward, except for the throws java.io.IOException clause appended to the main() method header. You must include the clause because System.in.read can throw, or pass, an exception — an object that describes a problem — to the JVM. (You will explore exceptions in detail in a future article.)

How do you use WordCount? Consider this example: count the number of words in a file called memo.txt by issuing the following command line: java WordCount <memo.txt.

Review

This article sheds the mystery from standard I/O. From a debugging perspective, you will find this simple mechanism to be helpful when developing Java programs — even programs that present GUIs.

Next month, Java 101 picks up the pace when you begin exploring the object-oriented entities of the Java language.

Jeff Friesen has been involved with computers for the past 20 years. He holds a degree in computer science and has worked with many computer languages. Jeff has also taught introductory Java programming at the college level. In addition to writing for JavaWorld, he has written his own Java book for beginners — Java 2 By Example, Second Edition (Que Publishing, 2001; ISBN: 0789725932) — and helped write Special Edition Using Java 2 Platform (Que Publishing, 2001; ISBN: 0789720183). Jeff goes by the nickname Java Jeff (or JavaJeff). To see what he’s working on, check out his Website at http://www.javajeff.com.