- COMP.CS.140
- 3. Java as Programming Language
- 3.1 More basic features of Java
- 3.1.1 Java input and output streams
Java input and output streams¶
A Java virtual machine (JVM) automatically initializes the output objects System.out
and
System.err
and the input object System.in
as public member class variables of the
class System
. These have similar roles as e.g. the C++ stream objects cout
, cerr
and cin
. System.out
prints to the standard output (e.g screen), System.err
to the
standard error output (e.g. screen), and System.in
reads from the standard input (e.g.
keyboard).
To be more precise, System.out
and System.err
are objects of the class
java.io.PrintStream
. The most commonly used printing functions of this class are probably
println
and print
, which print out a single value given to them as a parameter. The
functions automatically convert a non-string parameter into a String
. The only difference
betweeen the two functions is that println
prints out a new line after the value and
println
can also be called without a parameter (it will then print a single new line). A third
quite commonly used printing function is format
, which is a Java counterpart of the C function
printf
. Note that also Python’s counterpart to printf
is named format
.
PrintStream
does in fact contain also a function named printf
, but it is
identical with format
and we have chosen to use this latter function name in this material.
The format
function takes a so-called “format string” parameter that allows to embed variable
values into the printed string. The format string is a String
that is otherwise printed as
such, but it may contain special format specifiers that start with the percent character ‘%’. The
format specifiers (usually) specify that format
should replace the specifier with the value of
a corresponding separately provided function parameter. This implies that such replacing parameters
must be given to printf
as further parameters after the format string. Each specifier defines
the type of the replacing value. For example %s
means a string, %d
an integer and %f
a
floating point value. Format specifiers can also contain further formatting modifiers that e.g.
define a width or precision to use when embedding that particular value. Basic examples are given
below. See the documentation of
Formatter
for further details about available specifiers and formatting parameters.
String name = "Lionel Messi"
int birthYear = 1987;
double height = 1.69;
System.out.format("Name: %s, Birth year: %d, Height: %.2f%n", name, birthYear, height);
System.out.format("Pi with three decimals and 10-character width: %10.3f", Math.PI);
Here e.g the specifier “%.2f” means a floating point value printed with two decimals, and “%10.3f” a floating point value printed with three decimals and width of 10-characters (padded with spaces). In addition the specifier “%n” means a system specific new line: for example “n” on Linux and “rn” on Windows. The code snippet would print:
Name: Lionel Messi, Birth year: 1987, Height: 1.69
Pi with three decimals: 3.142
The PrintStream
class can also be used for printing into files: if we give a string as a
constructor parameter when creating a new PrintStream
object, the resulting object will
print into a file named by the string parameter. Note that if the file already exists, its
previous contents will be overwritten. If we want to avoid that, we need to first create a
java.io.FileOutputStream
file outputstream object in append mode and then use that object
(instead of a string) as the PrintStream
constructor parameter.
System.in
is an object of class java.io.InputStream
. InputStream
is an abstract
input stream class that is suited for quite basic bytewise input operations. The class
java.io.BufferedReader
provides more convenient operations for reading text data, such as
the member function readLine
that reads a single input line. In order to use
BufferedReader
, we must first create an unbuffered input stream object and then give it
as a constructor parameter for BufferedReader
. The class java.io.InputStreamReader
provides unbuffered reading operations from an input stream that can be specified as a constructor
parameter.
To sum up the preceding, we could for example construct a BufferedReader
object for reading
standard input as follows: new BufferedReader(new InputStreamReader(System.in))
. This kind
of a layered construct InputStream
→ InputStreamReader
→ BufferedReader
might
especially at first feel cumbersome, but this kind of constructs are very common in Java
(and quite common elsewhere, too).
BufferedReader
is convenient also for reading files. The only difference to the preceding
example is that we should provide the constructor of BufferedReader
with an unbuffered
file input stream object. This can be created easily using the class java.io.FileReader
that accepts the name of the file to read as a constructor parameter.
Using streams ⇒ usually need to consider exceptions
An error during an input or output stream operation, e.g. failing to open a file, can cause
a so-called exception. We will discuss exceptions later, but at this point it is worth
mentioning that Java in many cases requires us to somehow consider the possibility of
such exceptions: otherwise the Java compiler will give an error. Unless we implement an
explicit exception handler ourself, the exception will propagate from the current function
to its caller. In order to help the caller anticipate such a possibility, a Java function
definition must explicitly list the kind of exceptions that can propagate out from the
function. This kind of so-called exception specification is given at the end of the function
header in the form “throws exceptionType1, ..., exceptionTypeN
”. That is, the keyword
throws
followed by a list of exception types separated by commas. Java stream operations
usually cause (or throw) exceptions of type IOException
.
The preceding rule does not apply to all exceptions. Java exceptions have been divided into two broad categories: checked and unchecked exceptions. Only checked exceptions must be either handled explicitly or declared in an exception specification.
Full featured IDE’s (e.g. Netbeans) will automatically inform you about the types of checked exceptions that may occur in your code. You can also find that information by browsing the documentation of the libraries used in your code, or as a last resort, from error messages given by the Java compiler.
Files should be closed once you no longer need to access them. This can be done explicitly
by calling the member function close
of the stream object. A more recommended way is to
use Java’s so-called “try-with-resources” structure, which will automatically close the file.
Note that also Python contains a similar mechanism.
Below is an example program that illustrates stream input and output. The program first reads an input filename, line length limit and output filename from the standard input (ie. from keyboard). The program will then read the input file and write its contents to the output file in such manner that all words as separated with single spaces and no line is longer than the specified line length. The program stops when the user enters “quit”, and otherwise the program will proceed to again ask the user to give an input filenane, line length limit and an output filename.
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintStream;
public class LineWrap {
// The stream operations in the main function may cause an exception of type
// IOException. Therefore we have added an exception specification
// "throws IOException" to the end of its header.
public static void main(String args[]) throws IOException {
// Create a BufferedReader object "user" for reading System.in.
// Note that, unlike with files, we should not use try-with-resources here
// because the standard input stream should not be closed.
BufferedReader user = new BufferedReader(new InputStreamReader(System.in));
while(true) { // An infinite loop that will be exited upon a "quit" command.
System.out.print("Give input filename (or 'quit'): ");
final String inputFilename = user.readLine();
if("quit".equalsIgnoreCase(inputFilename)) {
break;
}
System.out.print("Give line wrap length limit (or 'quit'): ");
String lineLimStr = user.readLine();
if("quit".equalsIgnoreCase(lineLimStr)) {
break;
}
final int lineLim = Integer.parseInt(lineLimStr);
System.out.print("Give output filename (or 'quit'): ");
final String outputFilename = user.readLine();
if("quit".equalsIgnoreCase(inputFilename)) {
break;
}
// Also count statistics about input and output lines.
int inputLines = 0;
int outputLines = 0;
// The form of try-with-resources:
// try(resource variable statements separated by semicolons) {
// the code block that uses the resources
// }
// The resources listed by the resource variable statements will be closed
// automatically when the program exits the following code block. The resources
// must have a member function "close". In try-with-resources below we create
// BufferedReader input for file reading and PrintStream output for file writing.
// This is also an example where using the inferred var type might be ok: the
// conrete type has a longish name and is anyway spelled out in the new operation.
try(var input = new BufferedReader(new FileReader(inputFilename));
var output = new PrintStream(outputFilename)) {
String line = null;
int lineLen = 0;
while((line = input.readLine()) != null) {
inputLines += 1;
// Split interprets its parameter as a regular expression, where "\\s+"
// means one or more space characters (space, tabulator, etc.).
String[] words = line.split("\\s+");
for(String word : words) {
if(word.length() > 0) {
if(lineLen > 0 && lineLen + 1 + word.length() > lineLim) {
output.println();
outputLines += 1;
lineLen = 0;
}
if(lineLen > 0) {
output.print(" ");
lineLen += 1;
}
output.print(word);
lineLen += word.length();
}
}
}
if(lineLen > 0) {
output.println();
outputLines += 1;
}
}
// At this point input and output have been closed automatically.
// Print out input and output line statistics. This time as an example to System.err.
System.err.format("The input file %s had %d lines.%n", inputFilename, inputLines);
System.err.format("The wrapped file %s has %d lines.%n", outputFilename, outputLines);
}
}
}