Java Programming 5 | Input and Output

Series: Java Programming

Java Programming 5 | Input and Output

  1. I/O Devices

(1) The Definition of I/O Devices

The I/O devices are used to communicate the program with the outside world.

  • Typical Input Devices: keyboard, trackpad, storage, network, camera, microphone
  • Typical Output Devices: display, storage, network, printer, speakers

(2) The Definition of Terminal

An abstraction for providing input and output to a program.

(3) The Definition of Command-Line Input

An abstraction for providing arguments (strings) to a program. In a Java program, the inputs are the strings we type after the program name. They are referred to as an array of string elements by String[] args. These arguments are available as args[0] , args[1] , … at run time. If we want to convert the string inputs to some other data types, we have to call system conversion methods. For example,

Integer.parseInt(args[0]);
Double.parseDouble(args[1]);
...

(4) The Definition of Infinity

An abstraction describing something having no limit.

(5) The Definition of Standard Output Stream

An abstraction for an infinite output sequence.

(6) The Definition of Standard Input Stream

An abstraction for an infinite input sequence.

2. Standard I/O Library

In this section and the following articles in this series, we are going to use the standard I/O library instead of the inbuild methods like System.out. because with the standard library, we can make input and output independent of system, language, and locale. You can find the standard library that we are going to use in this link. And what we have to download is a file called stdlib.jar. Let’s first download this stdlib.jar file into our directory of programs. Then we unjar it into the current directory,

$ jar xf stdlib.jar

Then we can use the standard libraries like StdIn and StdOut .

(1) StdIn Library

The StdIn library can be used to deal with the standard input stream in an easy way.

  • read int
int readInt()
  • read Double
double readDouble()
  • read char
char readChar()
  • read string
String readString()
  • read boolean
Boolean readBoolean()
  • check no more inputs (true if it finds control + d)
Boolean isEmpty()
  • read all the rest of the inputs
String readAll()

(2) StdOut Library

  • print a string s on the output stream
void print(String s)
  • print a string s , then a new line on the output stream
void println(String s)
  • print a new line on the output stream
void println()
  • Formatted Output
void printf(String f, ...)

(3) Example 1: Two Add

Let’s now see a baby example. Suppose we have two integer inputs and the program will output the sum of these two numbers. The program should be,

public class AddTwo
{
public static void main(String[] args)
{
StdOut.print("Type the first integer: ");
int x = StdIn.readInt();
StdOut.print("Type the second integer: ");
int y = StdIn.readInt();
int sum = x + y;
StdOut.println("Their sum is " + sum);
}
}

Then we can try,

$ javac AddTwo.java 
$ java AddTwo
Type the first integer: 2
Type the second integer: 3
Their sum is 5

(4) Example 2: Average

In this program, we are supposed to read a stream of numbers and then compute their average. To specify the end of the input stream, we need to type control + d. The program should be,

public class Average
{
public static void main(String[] args)
{
double sum = 0.0; // cumulative total
int n = 0; // number of values
while (!StdIn.isEmpty())
{
double x = StdIn.readDouble();
sum = sum + x;
n++;
}
StdOut.println(sum / n);
}
}

We can check this program by,

$ java Average
10.0 5.0 6.0 3.0 7.0 32.0
<Ctrl-d>
10.5

3. Pipe

Let’s now see a common requirement of I/O. Suppose we have two programs and the output of the first program should be the input of the second program, what can we do to communicate between these two programs? Let’s see an example about averaging a random sequence.

(1) Random Sequence Program

The first program is ought to generate a sequence of random values. We have an argument N that decides how many random values we will generate. This program should be,

public class RandomSeq {
public static void main(String[] args) {
int N = Integer.parseInt(args[0]);
for (int i = 0; i < N; i++) {
StdOut.println(Math.random());
}
}
}

And a possible output can be,

$ java RandomSeq 5
0.85014557857813
0.40444737688684473
0.6986746723704673
0.26728965833924734
0.20516321909788282

(2) Average Program

The second program will simply read from a sequence of double values. This is just the same as what we have implemented above in the Average.java program. Again, the program should be,

public class Average
{
public static void main(String[] args)
{
double sum = 0.0; // cumulative total
int n = 0; // number of values
while (!StdIn.isEmpty())
{
double x = StdIn.readDouble();
sum = sum + x;
n++;
}
StdOut.println(sum / n);
}
}

(3) File Communication

If we want to communicate between two files, the simplest way is that we can firstly output the random result into a file data.txt, which can be read by the second program. We can try,

$ java RandomSeq 5 > /tmp/data.txt

This command will output a sequence of double values into the file /tmp/data.txt (we put the file in the /tmp directory so that it can be automatically deleted after rebooting). The symbol > means that we will redirect the standard output to somewhere else instead of the terminal. We can check the content of this file by,

$ more /tmp/data.txt
0.8475857238829543
0.6837067501758293
0.5577651407952862
0.054652767485671494
0.8133605698508706

To take the standard input from the file /tmp/data.txt, we have to use the < symbol. For instance, we can try the following command and we will be given the average value.

$ java Average < /tmp/data.txt
0.5914141904381223

(4) Limitations of File Communications

In the method above, we have used a file to communicate between two files. However, this method is limited by many concerns. First of all, this is not a straightforward solution to communicate between programs because an extra file is used for communicating. Secondly, this method is limited by the maximum size of the file, which means that it is possible we may not be able to use this method if we have a big data problem. The last limitation is that this method occupies too much storage and computation if we have much data. Therefore, generally speaking, we will not commonly use this method for program communications.

(5) Piping

To avoid saving data, we can directly redirect the standard output stream of one program to the standard input stream of another program, and this is called piping. It is represented by the symbol | . For example, we can calculate the average value of 100,000 random doubles by,

$ java RandomSeq 100000 | java Average
0.500614564728214

4. Drawing Abstraction

(1) StdDraw Library

We have talked about the standard input and the standard output, now we are going to talk about the standard drawing library StdDraw. Let’s first see the content of this library,

  • draw a line from (x0, y0) to (x1, y1)
void line(double x0, double y0, double x1, double y1)
  • draw a point (x, y)
void point(double x, double y)
  • draw a string s at (x, y)
void text(double x, double y, String s)
  • draw a circle at (x, y) with radius r
void circle(double x, double y, double r)
  • draw a filled circle at (x, y) with radius r
void filledCircle(double x, double y, double r)
  • draw a square at (x, y) with half-length r
void square(double x, double y, double r)
  • draw a filled square at (x, y) with half-length r
void filledSquare(double x, double y, double r)
  • draw a polygon with points at (x0, y0), (x1, y1), …
void polygon(double[] x, double[] y)
  • put a picture at (x, y)
void picture(double x, double y, String filename)
  • set pen radius
void setPenRadius(double r)
  • set pen color
void setPenColor(Color c)
  • set the scale of x coordinate axis to [x0, x1) or y coordinate axis to [y0, y1)
void setXscale(double x0, double x1)
void setYscale(double y0, double y1)
  • show the drawing after dt milliseconds
void show(int dt)

(2) HelloWorld Example for StdDraw

In this example, let’s draw an equilateral triangle with side length 1. The three vertices of this triangle is supposed to be (0, 0), (1, 0), and (0.5, sqrt(3)/2). We should also draw the center of this triangle, which should be (0.5, sqrt(3)/6). A “Hello, World” string should be put inside the triangle at (0.5, 0.5) and the pen radius should be 0.01. The program should be,

public class Triangle
{
public static void main(String[] args)
{
double c = Math.sqrt(3.0) / 2.0;
StdDraw.setPenRadius(0.01);
StdDraw.line(0.0, 0.0, 1.0, 0.0);
StdDraw.line(1.0, 0.0, 0.5, c);
StdDraw.line(0.5, c, 0.0, 0.0);
StdDraw.point(0.5, c/3.0);
StdDraw.text(0.5, 0.5, "Hello, World");
}
}

The output should be,

(3) Example 1: Map Data Visualization

Let’s continue to see more examples. You can download the data from this link. The first four doubles in this data file is the bounding box coordinates and the following data is the sequence of point coordinates of 13,509 cites. Let’s draw these data points. The program should be,

public class PlotFilter {
public static void main(String[] args) {
double xmin = StdIn.readDouble();
double ymin = StdIn.readDouble();
double xmax = StdIn.readDouble();
double ymax = StdIn.readDouble();
StdDraw.setXscale(xmin, xmax);
StdDraw.setYscale(ymin, ymax);
while (!StdIn.isEmpty())
{
double x = StdIn.readDouble();
double y = StdIn.readDouble();
StdDraw.point(x, y);
}
}
}

We can use the following method to run this code,

$ javac PlotFilter.java
$ curl https://introcs.cs.princeton.edu/java/15inout/USA.txt | java PlotFilter

(4) Example 2: Plotting A Function

In this example, we are planning to plot the function,

in the interval (0, π). To draw this plot, we will have to take regularly spaced N samples and then connecting them with each other. The program would possibly be,

public class PlotFunctionEx
{
public static void main(String[] args)
{
int N = Integer.parseInt(args[0]);
double[] x = new double[N+1];
double[] y = new double[N+1];
for (int i = 0; i <= N; i++)
{
x[i] = Math.PI * i / N;
y[i] = Math.sin(4*x[i]) + Math.sin(20*x[i]);
}
StdDraw.setXscale(0, Math.PI);
StdDraw.setYscale(-2.0, +2.0);
for (int i = 0; i < N; i++)
StdDraw.line(x[i], y[i], x[i+1], y[i+1]);
}
}

In this case, we have to select a relatively large sample so that we can plot this function in the correct way. For example, if we choose only 4 data points, we will have

$ java PlotFunctionEx 4

When we have more data points like 20, we will have,

$ java PlotFunctionEx 20

However, this plot is still not correct because we miss many data points between each pairs of neighbours. So suppose we have, 200 data points in this interval, we can draw a more accurate plot in this case.

$ java PlotFunctionEx 200

(5) Example 3: Chaos

Let’s now play a game. Suppose we have a triangle and the vertices are (0, 0), (1, 0), and (0.5, sqrt(3)/2), and we name these vertices as vertex 0, vertex 1, and vertex 2.

Now, let’s start with the current point at vertex 0. Let’s randomly pick a vertex (e.g. vertex 2) and then draw the point halfway between that vertex and the current point. Then this new point becomes the current point.

Repeat this process. If the next vertex we choose is vertex 1, we will have,

A more concrete description is illustrated as follows,

If we repeat this process for 100,000 times, than what can we have? Let’s write a program to see.

public class Chaos
{
public static void main(String[] args)
{
int trials = Integer.parseInt(args[0]);
double c = Math.sqrt(3.0) / 2.0;
double[] cx = { 0.000, 1.000, 0.500 };
double[] cy = { 0.000, 0.000, c };
StdDraw.setPenRadius(0.005);
double x = 0.0, y = 0.0;
for (int t = 0; t < trials; t++)
{
int r = (int) (Math.random() * 3);
x = (x + cx[r]) / 2.0;
y = (y + cy[r]) / 2.0;
StdDraw.point(x, y);
}
}
}

Then we can run this program by,

$ java Chaos 100000

This plot is called the Sierpinski triangles, which is the foundation of the fractal drawings. In conclusion, in order to make this plot, we need to have the following rules.

If we change the rules we have listed, we can get some fractals that are more natural.

(6) Example 4: Iterated Function System (IFS)

In mathematics, iterated function systems are a method of constructing fractals. By changing the rules of IFS, we can compute more natural fractals. The starting point of an IFS should always be (0, 0), and we will have different probabilities to choose from functions that can be used to generate the new point. The IFS program should be,

public class IFS {
public static void main(String[] args) {
        int trials = Integer.parseInt(args[0]);
        double[] dist = StdArrayIO.readDouble1D();
        double[][] cx = StdArrayIO.readDouble2D();
double[][] cy = StdArrayIO.readDouble2D();
        double x = 0.0, y = 0.0;
        StdDraw.enableDoubleBuffering();
for (int t = 0; t < trials; t++) {
            int r = StdRandom.discrete(dist);
            double x0 = cx[r][0]*x + cx[r][1]*y + cx[r][2]; 
double y0 = cy[r][0]*x + cy[r][1]*y + cy[r][2];
x = x0;
y = y0;
            StdDraw.point(x, y);
            if (t % 100 == 0) {
StdDraw.show();
StdDraw.pause(10);
}
}
        StdDraw.show();
}
}

For example, if we want to change the rules to the rules for generating a coral, which should be,

And the data of coral can be found from this link,

$ curl https://introcs.cs.princeton.edu/java/22library/coral.txt | java IFS 100000

Or, we can also have some fractals of ferns, like

$ curl https://introcs.cs.princeton.edu/java/22library/barnsley.txt | java IFS 100000

(7) Example 5: Bouncing Ball Animation

Let’s suppose we have a ball bouncing in a light grey square of length 2 (Xscale = -1 to 1 and Yscale = -1 to 1). The radius of the ball is 0.05. The starting point is (0.48, 0.86), and the initial speed is vx = 0.015, xy = 0.023. If the ball hits the wall that means in the next loop, the x position or y position of the ball will be larger than 1 or smaller than -1. According to the physical rules, if the ball hits a vertical wall, we will set vx to -vx. And if the ball hits a horizontal wall, we have to set vy to -vy. If the ball doesn’t hit the wall, we will update the position to (rx+vx, ry+vy) in the current loop and continue. The ball will be shown for 20 milliseconds in each loop, and after that, the ball will be cleared, and a new position of the ball will be drawn.

The program is,

public class BouncingBall
{
public static void main(String[] args)
{
double rx = .480, ry = .860;
double vx = .015, vy = .023;
double radius = .05;
StdDraw.setXscale(-1.0, +1.0);
StdDraw.setYscale(-1.0, +1.0);
while(true)
{
StdDraw.setPenColor(StdDraw.LIGHT_GRAY);
StdDraw.filledSquare(0.0, 0.0, 1.0);
if (Math.abs(rx + vx) + radius > 1.0) vx = -vx;
if (Math.abs(ry + vy) + radius > 1.0) vy = -vy;
rx = rx + vx;
ry = ry + vy;
StdDraw.setPenColor(StdDraw.BLACK);
StdDraw.filledCircle(rx, ry, radius);
StdDraw.show(20);
}
}
}

Now, let’s add more features to the ball. We would like to replace the filled circle with the picture of a real tennis ball and when the ball hits the wall, we will hear a “bang” audio at the same time. You can download these files from the links that are given. Remember that we need to use the StdAudio library. Finally, the program is,

public class BouncingBallDeluxe
{
public static void main(String[] args)
{
double rx = .480, ry = .860;
double vx = .015, vy = .023;
double radius = .05;
StdDraw.setXscale(-1.0, +1.0);
StdDraw.setYscale(-1.0, +1.0);
while(true)
{
StdDraw.setPenColor(StdDraw. LIGHT_GRAY);
StdDraw.filledSquare(0.0, 0.0, 1.0);
if (Math.abs(rx + vx) + radius > 1.0)
{ StdAudio.play("pipebang.wav"); vx = -vx; }
if (Math.abs(ry + vy) + radius > 1.0)
{ StdAudio.play("pipebang.wav"); vy = -vy; }
rx = rx + vx;
ry = ry + vy;
StdDraw.picture(rx, ry, "TennisBall.png");
StdDraw.show(20);
}
}
}