Today we're going to talk about input and output. Most of the Java programs that we've written so far are mostly inwardly focused. Where we generate random numbers with a program and maybe write code that computes statistics from it. In the real world, what we probably want to do is process somewhere from the real world. Take it as input and then produce output that meaningfully expresses the result of the computation. So we're going to talk about Java mechanisms that allow us to this this along with some libraries that we've written. First, we're going to talk about something called standard input and output. We need a basic mechanism for communicating with your program. This adds, again, quite a bit to our basic building blocks for For programming. We've already talked about standard input and output for text, but now, in this lecture, we're going to open up a whole new world by talking about writing programs that can manipulate sound, and images, and graphics. And we'll have plenty of examples of that later on. So the goal is to write Java programs that interact with the outside world in some meaningful way, using input and output devices that are connected to the computer. There's all different types of input devices. We've been using the keyboard and maybe there's storage, or a trackpad, maybe the Internet itself, camera, microphone, all different types of devices exist and are connected to your computer for providing input, and we want to get that input to your program. And then we want to provide output, so we can do it on the display, or maybe in a storage device, or out to the network, or to a printer, or maybe speakers. So our approach to get this to happen for all these various devices is to define abstractions for input and output, and we'll talk about what that means. And then, here's the operation system functionality based on those abstractions to connect our jobs or programs to the actual devices that you might own. Abstraction plays a really essential role in understanding computation and we'll come back to it again in this course. But this is a fine example. An abstraction is something that exists only as an idea. So if you want to think more deeply about that take a philosphy course. We'll go at a level that's quite intuitive when we talk about this. So in this example, the idea of printing is, that's really, it's the idea. So far the way we've discussed it of a program producing text as output. So a good abstraction like this one simplifies our view of the world because it unifies lots of diverse, real-world artifacts. We're not specifying precisely how. The text gets printed and it's precisely which kind of device from the point of view of your program,we're just talking about printing and that's same program can produce results on all different types of devices. The obstruction that the program works with is printing. It's up to the operating system to realise that obstruction in the real world, depending on the actual device. So we're going to talk about a number of different abstractions besides just printing for delivering input to or receiving output from our programs. So a quick review, we already done this to get started, so we have the idea of a terminal which is an abstraction for providing type input and output to a program and we talk about the terminal window. That's an idea that's not actually a terminal in ancient times. They actually had a terminal called the VT100 and you interact with that terminal window in pretty much the same way that people used to interact with VT100's decades ago, but it's an abstraction, it's a idea. Let's type stuff toour program and let's get typed output. And we use that to run a program, provide some input on the command line, and get some output in the standard output stream. So input from command line, output from the standard output screen. So that's what we've done so far. Just a high level view, we have this mental model of what a java program does. It take some input from the command line arguments. And it produces output on a standard output screen. We're going to significantly enhance this diagram during this lecture. So command-line input is an abstraction for providing strings, really, to a program. The properties are the strings that you type after the program name are available at runtime. As args(0), args(1), and so forth, those arguments are available when the program begins execution. That's how command line input works, and we call this system conversion methods to convert those strings into integers or doubles or other types of data. So we had a program called RandomInt that takes its value N from the command line. And so that's an example of the use of the command line input abstraction. And we type in an integer and gives us random integer in that range. That's our review of commad line input now in standard output is the other extraction we've been using, so before we talk about standard output is the idea of infinity. No less than abstraction too. That describes something that has no limit. And that's important because the standard outputs stream is an abstraction for an infinite output sequence. That is an output sequence whos length has no limit. You just keep writing to standard output form the point of view of our program, there's no limit on how much it can write out. And so we take strings from System.out.println(). We add them to the end of the standard output stream. And the operating system connects that to the terminal application by default. So we have this program for printing a random sequence of integers. So this time, we take an integer N, and we print that many random numbers on standard output. Program takes the number to print from standard input, but otherwise, it has no real limit on the number of things that it might print, and if you wanted to print a million numbers on standard output, you could go ahead and do that. Those numbers might get printed in some other way, but that's from the point of view of the program. It just uses the abstraction known as the standard output stream. So now we're going to look at an improvement to that where we add an infinite input stream. So here's what we do, now we're going to provide something like standard output. Has no limit, but it's input in the program not just output. So how does standard input work? Again it's infinity, it has no limit so it's going to be an abstraction from infinitely Input sequence. Command line is kind of like that, but all appearing one line, then there might be limits. And it appears at the beginning. So the advantage of standard input over the command line is the new data can come while the program is executing, and we'll take a look at that. Same way as we print stuff out while the program is executing. And again, no limit on the amount of data we can provide to a program. The other thing the way that we've written the libraries is the conversion to primitive types is explicitly handled. And we'll show you how that works. We built a library for this course to implement standard input. This abstraction has been around since the 1970's. And we felt that for effective Java programming, it's important to have quick availability to standard input. It's built on basic Java mechanisms. But we wanted to make the ability to read infinite numbers, events, or doubles or strings easy for every Java programmer. So the software that you downloaded from our book site at the beginning of the course has this library. It's called StdIn, and it supports these methods. So there's isEmpty(), it is the input stream empty. And then there's readInt(), read a value of type Int. readDouble(), read the value of type double, readLong(), readBoolean(), char, string, and readAll() is read everything. Then there's a couple of other methods that you can find by looking at the documentation in the book around the book site. So now let's look at some programs that use these methods which are very natural in this documentation. To match the standard input, we also developed standard output. It's pretty much the same as the System.out. And from now, on we're going to use standard out instead of System.out. So, why do we do that? Well, we're going to put all our I/O abstractions right in one place for use. And so you can go right there to figure what you need to do for input and output. And we have more control over standard op because it's ours. And we can provide consistent, independent of the system output, where System.out has some dependencies on languages and locale and other things. So, StdIn and StdOut are our abstractions for infinite input stream, infinite output stream. We're going to use StdOut from now on, let's take a look, just warm up. So the first thing is to show that we can do interactive input with standard input. So what you can do is write code that prompts the user to type some input. And you can also mix the input with the output stream. So this is just a very simple example if you want to add two numbers. So you can print("Type the first integer: "), that's a prompt to the user, and then read the integer that the user types. StdIn.ReadInt(), and that results in an integer value that we assigned to the variable x. And then print("Type the second integer: "), and get the second integer. Then you can compute the sum in the program and then print that their sum is and then print the answer. So, that's a simple program where we mix input and output on StdIn and StdOut. So, if we run this program, the program runs before we provide any data. Then we say, well, let's add 1 and 2, and it'll say their sum is 3, very simple application. Let's look at a more interesting one. Let's average the numbers on the standard input stream. So what we're going to do, we want to read the stream of numbers and compute their average. So here's the code for doing that. We're going to have a double variable sum that keeps track of the cumulative total of all the numbers, and we'll count them. The integer variable n will be the number of values. So while standard input is not empty, we're going to want to read a double value, put that in the variable x. Update sum by adding x to sum, and update n, the number of numbers we've seen by increment. And we stay in that while loop reading numbers until StdIn is empty. There could be ten numbers or it could be 1 million or could be 1 billion, from the point of view of the program, there's no limit. It'll keep reading numbers from standard input until standard input is empty. And then after it finds that the input stream is empty, it's read all the numbers, then it just prints out the average sum divided by n. So Java average, we can type in some numbers. And the first question that comes in is, well, I can type in numbers, but how do I tell standard input that I'm at the end of the input stream? How does it know? Well, for decades, the standard has been, you type Ctrl+D. Nowadays, this is a little bit systems dependent. So you might have to look that up and try a few things for your system. For example, on Windows, it's Ctrl+Z. But once you get by that, easy to specify the end of the input stream, and then it'll just print out the average. So this program has no dependency on the number of things in the standard input stream. From the point of view of the program, that's infinitely long. That's a very key point. It means that we can write a program today and still have it used many years later when there's much more possibility of much more input. Many people have written programs decades ago that were intended to handle just hundreds or even thousands of data points. Those same programs can now handle million or billion data points with absolutely no change because of the standard input abstraction. And again, input and output can be interleaved, we have to remember that. So here's the summary. We've seen two prototypical applications of standard input and standard output. For StdOut, it was generate a stream of random numbers. And for StdIn, it was compute the average of a stream of numbers. This one puts out as many numbers as it wants. This one takes in as many numbers as are there. And the key point to remember is that both streams are infinite. There's no limit on their size. So natural question comes up is, do I always have to type in my input data and print my output in the terminal window? And the answer to that, of course, is no. What you want to do and what people will always do is keep the data and results in files on your computer, or maybe some external device. And we can talk about that. And then there's the concept maybe you don't even have to do that. There's the idea of piping to connect programs. Again, these ideas go back to Unix in the 1970s. But they're still broadly useful where providing the mechanisms for you to use them within your Java programs. So let's look at how we can put results in files and how we can connect programs together. So keeping files on your computer takes advantage of a mechanism called redirection, which is supported by your operating system. On the command line, if you put a greater than sign, what it means is redirect standard output to the file that's name after the greater than sign. So if we say java RandomSeq 1000000 > data.text, we don't get anything printed in the terminal window. Because we redirected the output to go to that file. Then later, if we look at what's in that file, there's the 1 million numbers. Very simple way to get data produced by your program into a file, redirect standard output. So this is just a schematic of that. A program, RandomSeq, produces output, the standard output stream gets redirected to a file, instead of to the terminal window. And the mirror image of that is you can redirect from a file into standard input. So if we use a less than sign after the program name on the command line, that says, take standard input from the file that I'm going to name. So if we run java Average and take standard input from that file with a million numbers, we get a result that's close to a half, that we hope. So that's connecting the programs through the file. The file goes on its standard input stream into average. So that's an extremely convenient way to compute, and we're going to see lots of examples where you want to do that. There's still a slight problem there, so we don't really have that concept of infinity, because the maximum file size is going to restrict us in any real kind of situation. So we want to have this other idea called piping, where we don't save the data at all. It's just the abstraction of standard input and standard output lets us compute with an unlimited amount of data. Maybe we're only limited by time. So there's no room for a huge file on a computer, so what we do is use piping, and what that does is just connect the standard output of one program to the standard input of another. So in our example, if, after you issue a command, you put a vertical bar, that means set up a pipe. And it does exactly what this says, connect the standard output of that program to the standard input of the other one. So now we're generating a million numbers, but we're not saving them anywhere, we're not printing them on the terminal. All we're doing is providing each number to the other program when it asks for it. So now we can run this experiment for a million numbers a lot of times, or a billion numbers, without ever saving the numbers. We connect together the standard input stream and the standard output stream. Again, this is a very convenient way to compute. Neither program is concerned with the amount of data that it's going to process, but we have the ability to have the programs process an unlimited amount of data. No limit within the programs in the amount of data that they can handle. An awful lot of old programs that were written this way, that are still useful because of the standard input and standard output abstractions. It's the job of the system to figure out how to collect the data, temporarily, on standard output, and provide it to the other program on standard input. And there might be some restrictions or some difficulties in doing so, but the whole point, or the whole job of the system is to provide this abstraction for the programs. There's lots of other abstractions that systems provide for programs. This one is a particularly convenient one, having to do with input and output of text. And it's interesting to contemplate, because nowadays we're awash in data, and we need to consider algorithms of this type all the time, but it mirrors what people faced in early computing. In early computing, there was very little memory available. Each bit had to be physically touched by somebody, and after a while, there could be more memory, but the amount of memory available was way smaller than the amount of data that had to be processed. That's what led to the development of these abstract mechanisms, because there were dramatic increases happening every year. And with redirection and piping, it means that your program could handle way more data than could be stored in the computer, and we'll look a little more at that later on. So you could have standard input and standard output connected to devices that are external to the computer, for example. And that's an example of one, a paper tape punch, and we'll look at that later on in the course. So standard output would be connected to a paper tape punch that put the data on this physical medium outside the computer. Again, the amount of data is only limited by the amount of tape that you have, and then standard input would connect this paper tape reader to a standard input. And then the program could read unlimited amount of data, again, only limited by the amount of tape there is. And that was extremely effective in building up more complicated programs and more complicated data processing applications. But in modern computing, it's the same thing. We have the web, we have oceans of information. There's no way that you're going to fit the data that you want to process, in many, many applications, in any available computer memory. And we're still getting dramatic increases every year in the amount of data that's out there that we want to process. So what they're called now is streaming algorithms, where our program can handle a lot more data than our computers can store. But they're based on getting started with abstractions, like standard input and standard output. So the lesson is, whenever we can, we're going to try to avoid limits within our program whenever possible.