Chapter 11 Advanced Input/Output: Streams, Files, and Networking 11.1 Streams, Readers, and Writers • Stream are I/O abstractions. • Byte streams for machine-formatted data • Character streams for human-readable data. • Characters can get a little more complicated since Java uses 16-bit Unicode characters, on top of a system that uses mostly ANSI/ISO 8-bit characters Streams for byte I/O • OutputStream: an abstract class that is used to create objects (via subclasses) that output data to a byte stream. • InputStream: an abstract class that is used to create objects (via subclasses) that read data from a byte stream. For Character I/O • Writer : an abstract class used to create objects that output data to a character stream. • Reader: an abstract class used to create objects that read data from a character stream. • Characters are stored in the computer as 16-bit Unicode values. For people who use Western alphabets, character data is generally stored in files in ASCII code, which uses only 8 bits per character. • The Reader and Writer classes take care of this translation, and can also handle non-western alphabets in countries that use them. System.in and System.out • For historical reasons, System.in and System.out are byte streams rather than character streams. • You should use Readers and Writers rather than InputStreams and OutputStreams when working with character data, even when working with the standard ASCII character set. Primitive I/O • InputStream provides one method: .read(), that reads and returns one byte (0-255) or -1 if there is no more data (End Of File: EOF) • It also throws IOException, so you can only use it in try statement or in a subroutine that is itself declared with a “throws IOException” clause. • Reader is similar for characters. • For writing we have .write(int b) Higher-level output (for characters) • PrintWriter is a subclass of Writer, and has these methods: • out.print(x) — prints the value of x, represented in the form of a string of characters • out.println(x) — outputs the value of x, followed by an end-of-line • out.printf(formatString, x1, x2, ...) — does formatted output of x1, x2, ... to the output stream. The first parameter is a string that specifies the format of the output. There can be any number of additional parameters, of any type, but the types of the parameters must match the formatting directives in the format string. • out.flush() — ensures that characters that have been written with the above methods are actually sent to the output destination. In some cases, notably when writing to a file or to the network, it might be necessary to call this method to force the output to actually appear at the destination. (buffering!) • checkError() – error handling is different for PrintWriter. No exceptions, no need for try or throws. 11.1.3 Data Streams • Don’t worry about this for now. This stores the “raw bits and bytes” into a file, and requires using matching writers and readers. 11.1.4 Reading Text • .readLine() returns a string, or null. • Traditionally, Unix computers, including Linux and Mac OS X, use a line feed character, ’\n’, to mark an end of line; classic Macintosh used a carriage return character, ’\r’; and Windows uses the twocharacter sequence “\r\n”. In general, modern computers can deal correctly with all of these possibilities. • TextReader is a class written by the author, and is in TextIO. • Scanner is mostly useful for early learning exercises, and provides tools to read different types of data. 11.1.6 Serialized Object I/O • A somewhat more advanced system that allows writing and reading objects. 11.2 Files • In most languages we open and close files. • In Java, the open is handled by instantiating an object to read or write the file. A .close() method for the object is used to close the file. • The opening usually involves instantiating a stream object and passing it to a higher-level constructor to make an object to do the reading or writing. • We’ll skip the author’s TextReader class. You can read about it and use it if you like. 11.1.5 The Scanner Class • We’ve used this a little. • We create a new object of type Scanner, constructing with Standard.in to read from the keyboard. • We can also use any other character stream (like a file). • Scanner uses tokens, things separated by whitespace. • Can parse tokens of various primitives, and has “look ahead” methods .hasNextxxx() 11.1.6 Serialized Object I/O • If you implement Serializable, then Java makes it very easy to save and read objects quickly. • This is most useful if the objects are actually collections of other objects. • More on this later when we deal with such data structures. 11.2 Files • You should be familiar with the basic concepts of files and folders already. • C:\users\dave\documents\java\notes.txt - this is a fully-qualified file name. • C: is the drive • C:\users\dave\documents\java\ is the path of the folder where the file is • notes.txt is the filename, sometimes we would call the filename notes and the extension txt • Other operating systems tend to use “/” instead of Windows’ “\” How to open a text file for reading FileReader data; // (Declare the variable before the // try statement, or else the variable // is local to the try block and you won’t // be able to use it later in the program.) try { data = new FileReader("data.txt"); // create the stream } catch (FileNotFoundException e) { ... // do something to handle the error---maybe, // end the program } Some reality considerations • That "data.txt" in the example is a bit simplistic. In a console environment, the concept of “current directory” makes a lot of sense. In a GUI environment, not so much. Usually we’ll want to open a file with a fully-qualified file name. • Putting fully-qualified file names in code requires doubling the backslashes, and file names in code isn’t such a great idea, anyway. • Asking for a user to type in such a monster is unrealistic – so in practice we’ll want to use a “Chooser” that lets users point and click to select a file. The Chooser object will then give us a fully-qualified file name. • Sometimes you put all the file stuff in a single try block What folder am I in? • System.getProperty("user.dir") — returns the absolute path name of the current directory as a String. • System.getProperty("user.home")—returns the absolute path name of the user’s home directory as a String. • These can be used to allow users to enter simple file names, but a Chooser is a much better user interface. The File class • new File(String) creates a File object from a path name. • file.exists() — This boolean-valued function returns true if the file named by the File object already exists • file.isDirectory() • file.delete()—Deletes the file, if it exists. Returns a boolean value to indicate whether the file was successfully deleted. • file.list() — If the File object refers to a directory, this function returns an array of type String[] containing the names of the files in that directory. Otherwise, it returns null. The method file.listFiles() is similar, except that it returns an array of File instead of an array of String 11.2.3 File Dialog Boxes • new JFileChooser( File startDirectory or String pathToStartDirectory ) • showOpenDialog returns an integer that can be compared to JFileChooser.APPROVE_OPTION (or other constants_. • showSaveDialog also returns the same indication • You can also “filter” extensions to show in the dialog boxes. It is a bit tricky. See Google for samples. Actual code to find and read a file BufferedReader data; try { JFileChooser jfc = new JFileChooser(); jfc.showOpenDialog(null); // open the file by creating a FileReader object and passing it to the // BufferedReader constructor System.out.println("file name is: " + jfc.getSelectedFile()); data = new BufferedReader(new FileReader(jfc.getSelectedFile())); String l = data.readLine(); System.out.println(l); data.close(); // practice good housekeeping } catch (Exception e) { // do something to handle the error } Writing a file isn’t much harder PrintWriter data; try { JFileChooser jfc = new JFileChooser(); jfc.showSaveDialog(null); // open the file by creating a PrintWriter object System.out.println("file name is: " + jfc.getSelectedFile()); data = new PrintWriter(jfc.getSelectedFile()); data.println("Output in a file!"); data.close(); // practice good housekeeping } catch (Exception e) { // do something to handle the error } 11.3 Programming With Files • A whole copy program in one line: (sort of ) while ((data = source.read()) >= 0 ) copy.write(data); • Command-line arguments make Unix-like copy okay: java CopyFile work.dat oldwork.dat • But this sort of user interface is not much fun in a GUI environment. • There is a good copy example in the book. 11.3 Programming With Files, continued • 11.3.2 Persistent Data: an example of how to use a file with a specified name in the user’s home directory. • 11.3.4 Storing Objects in Files: use “implements Serializable” to make for quick save and restore of an ArrayList. Of course, we haven’t talked about ArrayList yet… 11.4 Networking • Good stuff for a Java II class… • URL class looks like fun. • 11.4.2 TCP/IP and Client/Server • 11.4.3 Sockets in Java (whew: getting through firewalls and other anti-malware software is a real pain). • 11.4.4 A Trivial Client/Server: a nice example of client/server • 11.4.5 A Simple Network Chat: another good example. 11.5 A Brief Introduction to XML • 11.5.1 Basic XML Syntax: a nice example of a plausible XML document, perhaps an SVG. • 11.5.2 Working With the DOM