Standard Input, Output, and Error in Java

Advertisement

Advertisement

Overview

Introduction

When writing command line applications in Java, you may want to prompt the user for input or a password, process a file, or pipe the output of another process through your application. This tutorial will walk through the process of using standard input, output, and error in Java.

If you are not familiar with standard streams, and file descriptors 0, 1, and 2, read up on https://en.wikipedia.org/wiki/Standard_streams. For this tutorial, Java 1.8 JDK is used.

One thing many developers overlook is the ability to write to standard output and standard error as different streams. If your application needs to print debug or other information while also outputting clean results, you should separate the output between standard output and standard error.

Standard output should be reserved only for your clean output that can be piped to a file or another process without error messages. Standard error should be used for errors, warnings, debug information, and anything you want the user to see, but you don't want to be included as part of the final output of the program. For example, let's say you have an application that outputs a series of strings that you pipe through to grep like this:

java MyProgram | grep "keyword"

In use cases like this, you may want to print an error or a warning to user that should not be included in the output to the next process (grep in this example). This is a good situation where you would continue piping standard output to the next process in the chain, but stderr would still print to the terminal for the user to see without being processed by grep.

By default, standard output and standard error both print to the terminal, so it's hard to distinguish them, but it becomes painfully obvious when you try to pipe output to another application or a file.

Java represents the standard streams with the following objects that can be read from and written to. Rememeber, these streams are treated just like files, even though the input may come from a keyboard, a file, or another process, and the output may go to the screen, a file, or another process.

// Java standard streams
System.out   // stdin,  file descriptor 0
System.in    // stdout, file descriptor 1
System.err   // stderr, file descriptor 2

You may already be familiar with System.out.println, a common function used to write data to standard output. For many developers, that is as far as they have explored the standard streams though. Let's dive in and look at what we can do.

Reading binary data from standard input

Since System.in is just a regular InputStream we can read directly from it using System.in.read(). You can see what methods are available for an InputStream using the official documentation at https://docs.oracle.com/javase/8/docs/api/java/io/InputStream.html. InputStreams don't have a huge selection of methods, but they get the simple jobs done. This next example demonstrates some basic operations by checking how many bytes are available, and then reading a single byte, followed by reading in to a buffer.

// ReadBytes.java
class ReadBytes
{
    public static void main(String[] args) throws Exception
    {
        // See approximately how many bytes are waiting to be read
        int numBytesWaiting = System.in.available();
        System.out.println("Bytes waiting: " + numBytesWaiting);

        // Read a single byte (an int from 0-255)
        int singleByte = System.in.read();
        System.out.println("The first byte of standard input is: " + singleByte);

        // Read a buffer's worth of bytes at once
        byte[] buffer = new byte[4];
        int offset = 0;
        System.in.read(buffer, offset, buffer.length);

        System.out.println("Next 4 bytes: " + new String(buffer));
    }
}

You can run it from the command line by itself and enter a string from the keyboard, or you can feed the program a file like this:

java ReadBytes < in.dat

While I describe this technique as reading binary data, it applies equally to text input, considering text input is still binary input. However, if you want to read simple text, I describe a better technique in the next section using a Scanner. I recommend using this technique when you are working with binary files.

Prompting user for input

If you are writing an interactive application and you want to prompt to user to input some values, you can create a Scanner object that wraps System.in. A Scanner provides all kinds of convenient methods for reading an input stream. Instead of having to read one byte at a time and process everything ourself, we can have it find strings, integers, and other data one element at a time. By default it will use spaces and newline characters as separators when searching for elements, but you can modify that to be a comma if you are using a CSV, or any other kind of delimiter.

// UserInput.java
import java.util.Scanner;

class UserInput
{
    public static void main(String[] args)
    {
        // Create a scanner to wrap the standard input stream
        Scanner scanner = new Scanner(System.in);

        // Prompt user to enter a string and press enter
        System.out.print("Enter your name: ");
        System.out.println("Hello, " + scanner.nextLine());

        // Use the scanner to then get a numeric value from the user.
        System.out.print("Enter an integer: ");
        System.out.println("Your number plus 1 is: " + (scanner.nextInt() + 1));       
    }   
}

There are also next* methods for the various data types. For example, nextByte(), nextBoolean(), nextBigDecimal(), etc. Refer to the Scanner documentation for more details.

Prompting user for a password

If you want to prompt the user for a password or other sensitive information, you may not want to echo the keys that the user is pressing on screen. Since Java 1.6, there has been a java.io.Console class with a readPassword() method that does exactly this. You can get the current console from System object using System.console().

It's worth noting that the Constole class has other methods similar to the ones we already saw. For example, Console has a readLine() method, and a printf() method, but that's about it. It is not as flexible as using a Scanner for input. The only thing it really offers is the convenient readPassword() method.

Read more about the java.io.Console class at https://docs.oracle.com/javase/8/docs/api/java/io/Console.html/

// GetPass.java
import java.io.Console;

class GetPass
{
    public static void main(String[] args)
    {
        Console console = new Console();
        char[] passwd = console.readPassword("Enter the secret password: ");

        // Now do something with passwd
    }
}

Reading standard input line-by-line

If you have a file being piped in to your application, you may want to continue processing every line as long as their is data. To do this you can utilize the Scanner described in the previous section to read line-by-line. This program will read line after line until it reaches an end of file (EOF) and print out the line it sees. It behaves similar to cat in Linux and type in Windows.

// ReadLines.java
import java.util.Scanner;

class ReadLines
{
    public static void main(String[] args)
    {
        Scanner scanner = new Scanner(System.in);
   
        // Read and print out each line.
        while (scanner.hasNextLine()) {
            String lineOfInput = scanner.nextLine();
            System.out.println(lineOfInput);
        }
    }
}

Writing to standard output

While we're talking about streams, most people already know how to print out to standard output using System.out.println(), but there are also other methods. For example, you can use System.out.print() to print out content without creating a newline at the end. You can also use System.out.printf() to print C-style format strings.

Standard out in Java, System.out is a PrintStream object, which is a subclass of OutputStream. The Java 8 documentation for a PrintStream is available at https://docs.oracle.com/javase/8/docs/api/java/io/PrintStream.html.

// Stdout.java
class Stdout
{
    public static void main(String[] args)
    {
        System.out.print("Hello, "); // No newline at end
        System.out.println("World!"); // With newline at end

        // Formatted string.
        System.out.printf("Int: %d, String: %s, UpperHex: %X", 23, "Test", 42);
    }
}

Writing to standard error

The standard error stream, System.err is a PrintStream just like System.out, so it has all of the same methods available. Everything you saw in the previous example regarding System.out can be repeated with System.err.

Running this example, the error output will likely go to your terminal output, and look just like standard output but it is being treated differently. Refer to the section below with examples of redirecting and piping the output and error streams separately.

// Stderr.java
class Stderr
{
    public static void main(String[] args)
    {
        System.err.print("Hello, "); // No newline at end
        System.err.println("World!"); // With newline at end

        // Formatted string.
        System.err.printf("Int: %d, String: %s, UpperHex: %X", 23, "Test", 42);
    }
}

How to redirect output

As mentioned earlier, you can separate the standard output and standard error streams, even though they are both printed out together to the terminal by default. You can also redirect one stream to another.

// Redirect standard output to file.txt. Will OVERWRITE output.txt if exists
java MyProgram > output.txt

// Append stdout to file, instead of overwriting
java MyProgram >> output.txt

// Redirect standard error to err.txt
java MyProgram 2> output.txt

// Redirect output to file, and then stderr to stdout. Both will write to file
java MyProgram > output.txt 2>&1

// In Linux, you can redirect all output to /dev/null if you don't want it at all
java MyProgram > /dev/null 2>&1

It is also possible to pipe the output of your program directly in to another program. This is done using the pipe character instead of the angle bracket.

// Redirect standard output to input another program
java MyProgram | grep "keyword"

How to redirect input

By default, standard input comes from the keyboard. You can instead pass a file as the input. It will pass along the file and the program won't know the difference that it came from a file instead of from the keyboard.
// Feed file.txt to MyProgram as standard input
java MyProgram < input.txt

Another option is to use the output of another program as the input. This is called piping and is the same example we saw just a moment ago when redirecting output through a pipe, except output program is on the receiving end this time.

// Pass stdout of another process as stdin to your program.
cat data_file.txt | java MyProgram

Conclusions

With this information, you should comfortably be able to manipulate the input, output, and error streams of your program to suit your needs. Take advantage of the redirection and concurrency the operating system provides when piping output through multiple programs and be a good citizen in the ecosystem by using the standard streams properly.

Advertisement

Advertisement