Often programs need to bring in information from an external source or send out information to an external destination. The information can be anywhere: in a file, on disk, somewhere on the network, in memory, or in another program. Also, it can be of any type: objects, characters, images, or sounds. To bring in information, a program opens a stream on an information source (a file, memory, a socket and reads the information serially, like this: !imilarly, a program can send information to an external destination by opening a stream to a destination and writing the information out serially, like this: "o matter where the information is coming from or going to and no matter what type of data is being read or written, the algorithms for reading and writing data is pretty much always the same. #eading $riting open a stream while more information read information close the stream open a stream while more information write information close the stream The java.io package contains a collection of stream classes that support these algorithms for reading and writing. These classes are di%ided into two class hierarchies based on the data type (either characters or bytes on which they operate. &owe%er, it's often more con%enient to group the classes based on their purpose rather than on the data type they read and write. Thus, we can cross(group the streams by whether they read from and write to data )sinks) or process the information as its being read or written. Overview of I/O Streams Character Streams Reader and Writer are the abstract superclasses for character streams in java.io. Reader pro%ides the A*+ and partial implementation for readers((streams that read ,-(bit characters((and Writer pro%ides the A*+ and partial implementation for writers((streams that write ,-(bit characters. !ubclasses of Reader and Writer implement speciali.ed streams and are di%ided into two categories: those that read from or write to data sinks (shown in gray in the following figures and those that perform some sort of processing (shown in white. The figure shows the class hierarchies for the Reader and Writer classes. /ost programs should use readers and writers to read and write information. This is because they both can handle any character in the 0nicode character set (while the byte streams are limited to +!O(1atin(, 2(bit bytes. Byte Streams (Binary Streams *rograms should use the byte streams, descendants of InputStream and OutputStream, to read and write 2(bit bytes. InputStream and OutputStream pro%ide the A*+ and some implementation for input streams (streams that read 2(bit bytes and output streams (streams that write 2(bit bytes. These streams are typically used to read and write binary data such as images and sounds. As with Reader and Writer, subclasses of InputStream and OutputStream pro%ide speciali.ed +3O that falls into two categories: data sink streams and processing streams. 4igure 5- shows the class hierarchies for the byte streams. As mentioned, two of the byte stream classes, ObjectInputStream and ObjectOutputStream, are used for object seriali.ation. !nderstandin" the I/O Superc#asses Reader and InputStream define similar A*+s but for different data types. 4or example, Reader contains these methods for reading characters and arrays of characters: int read() int read(char cbuf[]) int read(char cbuf[] int offset int len!th) InputStream defines the same methods but for reading bytes and arrays of bytes: int read() int read(b"te cbuf[]) int read(b"te cbuf[] int offset int len!th) Also, both Reader and InputStream pro%ide methods for marking a location in the stream, skipping input, and resetting the current position. Writer and OutputStream are similarly parallel. Writer defines these methods for writing characters and arrays of characters: void write(int c) void write(char cbuf[]) void write(char cbuf[] int offset int len!th) void write(Strin! s) And OutputStream defines the same methods but for bytes: void write(int c) void write(b"te cbuf[]) void write(b"te cbuf[] int offset int len!th) All of the streams((readers, writers, input streams, and output streams((are automatically opened when created. 6ou can close any stream explicitly by calling its close method. Or the garbage collector can implicitly close it, which occurs when the object is no longer referenced. $ow to !se %i#e Streams 4ile streams are perhaps the easiest streams to understand. !imply put, the file streams ( #ileReader, #ileWriter, #ileInputStream, and #ileOutputStream ( each read or write from a file on the nati%e file system. 6ou can create a file stream from a filename, a #ile object, or a #ile$escriptor object. The following program uses #ileReader and #ileWriter to copy the contents of a file named input.txt into a file called output.t%t : import java.io.&' public class (op" ) public static void main(Strin![] ar!s) throws IO*%ception ) #ile input#ile + new #ile(,input.t%t,)' #ile output#ile + new #ile(,output.t%t,)' #ileReader in + new #ileReader(input#ile)' #ileWriter out + new #ileWriter(output#ile)' int c' while ((c + in.read()) -+ ./) out.write(c)' in.close()' out.close()' 0 0 This program is %ery simple. +t opens a #ileReader on input.t%t and opens a #ileWriter on output.t%t. The program reads characters from the reader as long as there's more input in the input file. $hen the input runs out, the program closes both the reader and the writer. "otice the code that the (op" program uses to create a #ileReader: #ile input#ile + new #ile(,input.t%t,)' #ileReader in + new #ileReader(input#ile)' This code creates a #ile object that represents the named file on the nati%e file system. #ile is a utility class pro%ided by java.io. This program uses this object only to construct a #ileReader on input.t%t. &owe%er, it could use input#ile to get information about input.t%t, such as its full pathname. 1fter "ou2ve run the pro!ram "ou should find an e%act cop" of input.t%t in a file named output.t%t in the same director". #emember that #ileReader and #ileWriter read and write ,-(bit characters. &owe%er, most nati%e file systems are based on 2(bit bytes. These streams encode the characters as they operate according to the default character(encoding scheme. 6ou can find out the default character(encoding by using S"stem.!et3ropert" (,file.encodin!,). To specify an encoding other than the default, you should construct an OutputStreamWriter on a #ileOutputStream and specify it. 4or the curious, here is another %ersion of this program, (op"4"tes, which uses #ileInputStream and #ileOutputStream in place of #ileReader and #ileWriter : import java.io.&' public class (op"4"tes ) public static void main(Strin![] ar!s) throws IO*%ception ) #ile input#ile + new #ile(,input.t%t,)' #ile output#ile + new #ile(,output.t%t,)' #ileInputStream in + new #ileInputStream(input#ile)' #ileOutputStream out + new #ileOutputStream(output#ile)' int c' while ((c + in.read()) -+ ./) out.write(c)' in.close()' out.close()' 0 0 $ow to !se &ine'um(er)eader The 5ine6umberReader class is a subclass of 4ufferedReader. +ts read() methods contain additional logic to count end(of(line characters and thereby maintain a line number. !ince different platforms use different characters to represent the end of a line, 5ine6umberReader takes a flexible approach and recogni.es ,7n,, ,7r,, or ,7r7n, as the end of a line. #egardless of the end(of(line character it reads, 5ine6umberReader returns only ,7n, from its read() methods. 6ou can create a 5ine6umberReader by passing its constructor a Reader. The following example prints out all the lines of a file, with each line prefixed by its number. +f you try this example, you'll see that the line numbers begin at 8 by default: tr" ) #ileReader fileIn + new #ileReader(,te%t.t%t,)' 5ine6umberReader in + new 5ine6umberReader(fileIn)' while ((line+in.read5ine()) -+ null) ) S"stem.out.println(in.!et5ine6umber() 9 ,. , 9 line)' 0 0 catch (IO*%ception ioe) ) ioe.printStac:;race()' 0 The 5ine6umberReader class has two methods pertaining to line numbers. The !et5ine6umber() method returns the current line number. +f you want to change the current line number of a 5ine6umberReader, use set5ine6umber(). This method does not affect the stream position7 it merely sets the %alue of the line number. $ow to !se *rint+riter The 3rintWriter class is a subclass of Writer that pro%ides a set of methods for printing string representations of e%ery 8a%a data type. A 3rintWriter can be wrapped around an underlying Writer object or an underlying OutputStream object. +n the case of wrapping an OutputStream, any characters written to the 3rintWriter are con%erted to bytes using the default encoding scheme.9:; Additional constructors allow you to specify if the underlying stream should be flushed after e%ery line(separator character is written. The 3rintWriter class pro%ides a print() and a println() method for e%ery primiti%e 8a%a data type. As their names imply, the println() methods do the same thing as their print() counterparts, but also append a line separator character. The following example demonstrates how to wrap a 3rintWriter around an OutputStream: boolean b + true' char c + 2<2 double d + =.>/?@/ int i + ?A' Strin! s + ,R + ,' 3rintWriter out + new 3rintWriter(S"stem.out true)' out.print(s)' out.print(d)' out.println()' out.println(b)' out.println(c)' out.println(i)' This example produces the following output: R + =.>/?@/ true < ?A 3rintWriter objects are often used to report errors. 4or this reason, the methods of this class do not throw exceptions. +nstead, the methods catch any exceptions thrown by any downstream OutputStream or Writer objects and set an internal flag, so that the object can remember that a problem occurred. 6ou can <uery the internal flag by calling the chec:*rror() method. Although you can create a 3rintWriter that flushes the underlying stream e%ery time a line(separator character is written, this may not always be exactly what you want. !uppose that you are writing a program that has a character(based user interface, and that you want the program to output a prompt and then allow the user to input a response on the same line. +n order to make this work with a 3rintWriter, you need to get the 3rintWriter to write the characters in its buffer without writing a line separator. 6ou can do this by calling the flush() method. $ow to !se DataInputStream and DataOutputStream This page shows you how to use the java.io $ataInputStream and $ataOutputStream classes. +t features an example, $ataIO;est, that reads and writes tabular data (in%oices for 8a%a merchandise : import java.io.&' public class $ataIO;est ) public static void main(Strin![] ar!s) throws IO*%ception ) BB write the data out $ataOutputStream out + new $ataOutputStream(new #ileOutputStream(,invoice/.t%t,))' double[] prices + ) /C.CC C.CC /@.CC >.CC ?.CC 0' int[] units + ) /A = /> AC @8 0' Strin![] descs + ) ,Dava ;.shirt, ,Dava Eu!, ,$u:e Du!!lin! $olls, ,Dava 3in, ,Dava Fe" (hain, 0'
for (int i + 8' i G prices.len!th' i 99) ) out.write$ouble(prices[i])' out.write(har(27t2)' out.writeInt(units[i])' out.write(har(27t2)' out.write(hars(descs[i])' out.write(har(27n2)' 0 out.close()' BB read it in a!ain $ataInputStream in + new $ataInputStream(new #ileInputStream(,invoice/.t%t,))' double price' int unit' Strin! desc' double total + 8.8' tr" ) while (true) ) price + in.read$ouble()' in.read(har()' BB throws out the tab unit + in.readInt()' in.read(har()' BB throws out the tab desc + in.read5ine()' S"stem.out.println(,Hou2ve ordered , 9 unit 9 , units of , 9 desc 9 , at I, 9 price)' total + total 9 unit & price' 0 0 catch (*O#*%ception e) ) 0 S"stem.out.println(,#or a ;O;15 ofJ I, 9 total)' in.close()' 0 0 The tabular data is formatted in columns, where each column is separated from the next by tabs. The columns contain the sales price, the number of units ordered, and a description of the item, like this: /C.CC /A Dava ;.shirt C.CC = Dava Eu! $ataOutputStream, like other filtered output streams, must be attached to some other OutputStream. +n this case, it's attached to a #ileOutputStream that's set up to write to a file named invoice/.t%t. $ataOutputStream dos + new $ataOutputStream(new #ileOutputStream(,invoice/.t%t,))' "ext, $ataIO;est uses $ataOutputStream's speciali.ed writeXXX methods to write the in%oice data (contained within arrays in the program according to the type of data being written: for (int i + 8' i G prices.len!th' i 99) ) dos.write$ouble(prices[i])' dos.write(har(27t2)' dos.writeInt(units[i])' dos.write(har(27t2)' dos.write(hars(descs[i])' dos.write(har(27n2)' 0 dos.close()' "ote that this code snippet closes the output stream when it's finished. "ext, $ataIO;est opens a $ataInputStream on the file just written: $ataInputStream dis + new $ataInputStream(new #ileInputStream(,invoice/.t%t,))' $ataInputStream also must be attached to some other InputStream7 in this case, a #ileInputStream set up to read the file just written ( invoice/.t%t. $ataIO;est then just reads the data back in using $ataInputStream's speciali.ed readXXX methods. tr" ) while (true) ) price + dis.read$ouble()' dis.read(har()' BB throws out the tab unit + dis.readInt()' dis.read(har()' BB throws out the tab desc + dis.read5ine()' S"stem.out.println(,Hou2ve ordered , 9 unit 9 , units of , 9 desc 9 , at I, 9 price)' total + total 9 unit & price' 0 0 catch (*O#*%ception e) ) 0 S"stem.out.println(,#or a ;O;15 ofJ I, 9 total)' dis.close()' $hen all of the data has been read, $ataIO;est displays a statement summari.ing the order and the total amount owed, and closes the stream. "ote the loop that $ataIO;est uses to read the data from the $ataInputStream. "ormally, when reading you see loops like this: while ((input + dis.read5ine()) -+ null) ) . . . 0 The read5ine method returns a %alue, null, that indicates that the end of the file has been reached. /any of the $ataInputStream readXXX methods can't do this because any %alue that could be returned to indicate end(of( file may also be a legitimate %alue read from the stream. 4or example, suppose that you wanted to use (, to indicate end(of(file= $ell, you can't because (, is a legitimate %alue that can be read from the input stream using read$ouble, readInt, or one of the other read methods that reads numbers. !o $ataInputStream's readXXX methods throw an *O#*%ception instead. $hen the *O#*%ception occurs the while (true) terminates. $hen you run the $ataIO;est program you should see the following output: Hou2ve ordered /A units of Dava ;.shirt at I/C.CC Hou2ve ordered = units of Dava Eu! at IC.CC Hou2ve ordered /> units of $u:e Du!!lin! $olls at I/@.CC Hou2ve ordered AC units of Dava 3in at I>.CC Hou2ve ordered @8 units of Dava Fe" (hain at I?.CC #or a ;O;15 ofJ I=CA.== %i#e ,anipu#ation $hile streams are used to handle most types of +3O in 8a%a, there are some nonstream(oriented classes in java.io that are pro%ided for file manipulation. "amely, the #ile class represents a file on the local filesystem, while the Random1ccess#ile class pro%ides nonse<uential access to data in a file. +n addition, the #ilename#ilter interface can be used to filter a list of filenames. %i#e The #ile class represents a file on the local filesystem. 6ou can use an instance of the #ile class to identify a file, obtain information about the file, and e%en change information about the file. The easiest way to create a #ile is to pass a filename to the #ile constructor, like this: new #ile(,readme.t%t,) Although the methods that the #ile class pro%ides for manipulating file information are relati%ely platform independent, filenames must follow the rules of the local filesystem. The #ile class does pro%ide some information that can be helpful in interpreting filenames and path specifications. The %ariable separator(har specifies the system(specific character used to separate the name of a directory from what follows. +n a $indows en%ironment, this is a backslash (7, while in a 0"+> or /acintosh en%ironment it is a forward slash (B. 4ile separator can be obtained as !ystem.get*roperty('file.separator', which is how the 4ile class gets it. 6ou can create a #ile object that refers to a file called readme.t%t in a directory called m"$ir as follows: new #ile(,m"$ir, 9 #ile.separator(har 9 ,readme.t%t,) The #ile class also pro%ides some constructors that make this task easier. 4or example, there is a #ile constructor that takes two strings as arguments: the first string is the name of a directory and the second string is the name of a file. The following example does the exact same thing as the pre%ious example: new #ile(,m"$ir, ,readme.t%t,) The #ile class has another constructor that allows you to specify the directory of a file using a #ile object instead of a Strin!: #ile dir + new #ile(,m"$ir,)' #ile f + new #ile(dir ,readme.t%t,)' !ometimes a program needs to process a list of files that ha%e been passed to it in a string. 4or example, such a list of files is passed to the 8a%a en%ironment by the (51SS31;K en%ironment %ariable and can be accessed by the expression: S"stem.!et3ropert"(,java.class.path,) This list contains one or more filenames separated by separator characters. +n a $indows or /acintosh en%ironment, the separator character is a semicolon (', while in a 0"+> en%ironment, the separator character is a colon (J. The system(specific separator character is specified by the pathSeparator(har %ariable. Thus, to turn the %alue of (51SS31;K into a collection of #ile objects, we can write: Strin!;o:eniLer s' Mector v + new Mector()' s + new Strin!;o:eniLer(S"stem.!et3ropert"(,java.class.path,) #ile.pathSeparator)' while (s.hasEore;o:ens()) v.add*lement(new #ile(s.ne%t;o:en()))' 6ou can retrie%e the pathname of the file represented by a #ile object with !et3ath(), the filename without any path information with !et6ame(), and the directory name with !et3arent(). The #ile class also defines methods that return information about the actual file represented by a #ile object. 0se e%ists() to check whether or not the file exists. is$irector"() and is#ile() tell whether the file is a file or a directory. +f the file is a directory, you can use list() to get an array of filenames for the files in that directory. The canRead() and canWrite() methods indicate whether or not a program is allowed to read from or write to a file. 6ou can also retrie%e the length of a file with len!th() and its last modified date with lastEodified(). A few #ile methods allow you to change the information about a file. 4or example, you can rename a file with rename() and delete it with delete(). The m:dir() and m:dirs() methods pro%ide a way to create directories within the filesystem. /any of these methods can throw a Securit"*%ception if a program does not ha%e permission to access the filesystem, or particular files within it. +f a Securit"Eana!er has been installed, the chec:Read() and chec:Write() methods of the Securit"Eana!er %erify whether or not the program has permission to access the filesystem. %i#ename%i#ter The purpose of the #ilename#ilter interface is to pro%ide a way for an object to decide which filenames should be included in a list of filenames. A class that implements the #ilename#ilter interface must define a method called accept(). This method is passed a #ile object that identifies a directory and a Strin! that names a file. The accept() method is expected to return true if the specified file should be included in the list, or false if the file should not be included. &ere is an example of a simple #ilename#ilter class that only allows files with a specified suffix to be in a list: import java.io.#ile' import java.io.#ilename#ilter' public class Suffi%#ilter implements #ilename#ilter ) private Strin! suffi%' public Suffi%#ilter(Strin! suffi%) ) this.suffi% + ,., 9 suffi%' 0 public boolean accept(#ile dir Strin! name) ) return name.endsWith(suffi%)' 0 0 A #ilename#ilter object can be passed as a parameter to the list() method of #ile to filter the list that it creates. 6ou can also use a #ilename#ilter to limit the choices shown in a #ile$ialo!. )andom-ccess%i#e The Random1ccess#ile class pro%ides a way to read from and write to a file in a nonse<uential manner. The Random1ccess#ile class has two constructors that both take two arguments. The first argument specifies the file to open, either as a Strin! or a #ile object. The second argument is a Strin! that must be either ,r, or ,rw,. +f the second argument is ,r,, the file is opened for reading only. +f the argument is ,rw,, howe%er, the file is opened for both reading and writing. The close() method closes the file. ?oth constructors and all the methods of the Random1ccess#ile class can throw an IO*%ception if they encounter an error. The Random1ccess#ile class defines three different read() methods for reading bytes from a file. The Random1ccess#ile class also implements the $ataInput interface, so it pro%ides additional methods for reading from a file. /ost of these additional methods are related to reading 8a%a primiti%e types in a machine( independent way. /ultibyte <uantities are read assuming the most significant byte is first and the least significant byte is last. All of these methods handle an attempt to read past the end of file by throwing an *O#*%ception. The Random1ccess#ile class also defines three different write() methods for writing bytes of output. The Random1ccess#ile class also implements the $ataOutput interface, so it pro%ides additional methods for writing to a file. /ost of these additional methods are related to writing 8a%a primiti%e types in a machine( independent way. Again, multibyte <uantities are written with the most significant byte first and the least significant byte last. The Random1ccess#ile class would not li%e up to its name if it did not pro%ide a way to access a file in a nonse<uential manner. The !et#ile3ointer() method returns the current position in the file, while the see:() method pro%ides a way to set the position. 4inally, the len!th() method returns the length of the file in bytes.