Java I/O Notes: Reading and Writing Files using Streams and Reader/Writer Classes, Study notes of Computer Science

An introduction to java i/o, focusing on reading and writing files using byte streams and character-based reader/writer classes. It covers the differences between physical and virtual input sources, reading and writing text files, and using type wrappers to convert numeric strings. The document also introduces the tokenizer class for input processing.

Typology: Study notes

Pre 2010

Uploaded on 11/08/2009

koofers-user-ksp
koofers-user-ksp 🇺🇸

9 documents

1 / 13

Toggle sidebar

This page cannot be seen from the preview

Don't miss anything!

bg1
Introduction
In the previous three sections of Java notes you were introduced to the basics
of classes and methods in the Java language. This set of notes examines how
basic I/O in Java is handled.
The Basics
At the highest level Java makes a distinction between the stream classes
which handle binary 8-bit quantities, and the reader/writer classes which
manipulate string and 16-bit Unicode character values. Underneath each of
these classes is a vast array of subclasses (more than 40 by last count) which
actually handle the I/O.
A stream is simply an object for transmitting or retrieving 8-bit (byte) values
with the emphasis placed on the action of reading or writing rather than on the
data itself. A file is a collection of items stored on an external device and can
be accessed in several different fashions. For example, a FileStream provides
the capability of accessing the data values which are in a file but it does not
actually hold any of the file contents. A stream is always a pipeline for
transmitting 8-bit values, however, much of the functionality provided by the
different stream abstractions in Java is intended to allow the programmer to
think in terms of higher level units such as, transmitting strings or integers or
object values, instead of their 8-bit internal representations. For example, the
method readInt( ) which is found in the class DataInputStream processes four
8-bit values in the process of reading a single 32-bit integer value.
Java supports two independent, but largely parallel, I/O systems. The
hierarchy rooted in the two classes InputStream and OutputStream are used to
read and write 8-bit quantities. Until Unicode becomes much more prevalent
the parallel hierarchy rooted in the two classes Reader and Writer will be less
commonly used than the previous two classes. In this set of notes, we will
focus first on the stream classes and then on the reader/writer classes.
The differences in the various input stream classes are seen more readily if
they are divided into two broad categories, those classes which are tied to a
COP 3503 – Java Notes #4- 1
COP 3503 – Computer Science II – Java Notes #4
I/O in Java
pf3
pf4
pf5
pf8
pf9
pfa
pfd

Partial preview of the text

Download Java I/O Notes: Reading and Writing Files using Streams and Reader/Writer Classes and more Study notes Computer Science in PDF only on Docsity!

Introduction In the previous three sections of Java notes you were introduced to the basics of classes and methods in the Java language. This set of notes examines how basic I/O in Java is handled. The Basics At the highest level Java makes a distinction between the stream classes which handle binary 8-bit quantities, and the reader/writer classes which manipulate string and 16-bit Unicode character values. Underneath each of these classes is a vast array of subclasses (more than 40 by last count) which actually handle the I/O. A stream is simply an object for transmitting or retrieving 8-bit (byte) values with the emphasis placed on the action of reading or writing rather than on the data itself. A file is a collection of items stored on an external device and can be accessed in several different fashions. For example, a FileStream provides the capability of accessing the data values which are in a file but it does not actually hold any of the file contents. A stream is always a pipeline for transmitting 8-bit values, however, much of the functionality provided by the different stream abstractions in Java is intended to allow the programmer to think in terms of higher level units such as, transmitting strings or integers or object values, instead of their 8-bit internal representations. For example, the method readInt( ) which is found in the class DataInputStream processes four 8-bit values in the process of reading a single 32-bit integer value. Java supports two independent, but largely parallel, I/O systems. The hierarchy rooted in the two classes InputStream and OutputStream are used to read and write 8-bit quantities. Until Unicode becomes much more prevalent the parallel hierarchy rooted in the two classes Reader and Writer will be less commonly used than the previous two classes. In this set of notes, we will focus first on the stream classes and then on the reader/writer classes. The differences in the various input stream classes are seen more readily if they are divided into two broad categories, those classes which are tied to a COP 3503 – Computer Science II – Java Notes #

I/O in Java

physical input source and those which read from input streams which are virtual. The virtual streams depend upon a second input stream for the actual reading operations but in some way extend the functionality of the input stream. For example such a class might extend the functionality of the read operation by allowing values in the stream to be unconsumed (i.e., pushed back to the source) if desired. We will not look at the virtual stream classes. The physical input source classes read values from byte arrays, files, or pipes. We will focus here, on the file as the input source. Physical Input Streams There are three input stream classes that read from actual data areas. These are distinguished by their names and the arguments used in their constructors. These are summarized below: ByteArrayInputStream (byte [ ] buffer); ByteArrayInputStream (byte [ ] buffer, int offset, int count); FileInputStream (File f); FileInputStream (String fileName); PipedInputStream (PipedOuputStream p); For simple reading and writing, a FileInputStream or FileOutputStream can be manipulated without first creating a File object. Generally, a File object is necessary only if you are manipulating the file itself, such as renaming the file or deleting the file. If you need to test the file to determine if it is readable or writable, then a File object must be instantiated. The file I/O system of Java is contained in the package java.io and this package should be imported into your program using the import statement. Import java.io.*; How To Read In Character Data From the Keyboard System.in is an instance of InputStream so you automatically have access to the methods defined in InputStream. There is however, only one input method defined in this class, read( ), which reads bytes. When reading from System.in pressing the ENTER key generates an end-of-stream condition. Below is an example:

Example: Using read( ) to input values from a text file. // Program which prints the contents of a text file to illustrate reading //Usage: java DisplayFile import java.io.*; class DisplayFile { public static void main (String args [ ]) throws IOException { int i FileInputStream the_file; try { the_file = new FileInputStream(args[0]); } catch (FileNotFoundException exc) { System.out.println(“Couldn’t Find The File”); return; } catch (ArrayIndexOutOfBoundsException exc) { System.out.println(“Format is: DisplayFile ”); return: } do { i = the_file.read( ); //read a byte from the file if (i != -1) System.out.print( (char) i); } while (i != -1); the_file.close( ); } //end main } //end DisplayFile A couple of things are worth mentioning in the above example. Notice that the read( ) method returns an integer value. Typical implementations will define an integer to be 32 bits or 4 bytes. This is not a contradiction, rather only the low order 8 bits of the integer are used to return the value. If the value returned is equal to –1 then the end of the file has been reached. Since we are reading character data from the file, the integer value being read must be cast to a character. The try/catch blocks are handling the two most common types of errors that might occur with such a program, you can always add more, if other errors are possible when the program is executed. Writing to an output stream is similar to reading data from an input stream. The output stream is accepting bytes from the program to be written to the output file, one byte at a time. This of course can be tediously slow if large amounts of output are to be generated, so it is very common to buffer the output, sending it to the output file only when the buffer becomes full. The example below illustrates both reading and writing operations on text files using streams. The main difference between this example and the previous example is the addition of more try/catch blocks since exceptions can occur in two files in this program.

Example: Reading a text file and copying its contents to another text file. //Program to copy a text file into a second text file to illustrate reading and writing //Usage: java DuplicateFile import java.io.*; class DuplicateFile { public static void main (String args[ ]) throws IOException { int i; FileInputStream source; FileOutputStrean drain; try { try //try to open the source file { source = new FileInputStream(args[0]); } catch(FileNotFoundException exc) { System.out.println(“Didn’t Find Input File”); return; } try //try to open the drain file { drain = new FileOutputStream(args[1]); } catch(FileNotFoundException exc) { System.out.println(“Error Opening Output File”); return; } } //end try catch(ArrayIndexOutOfBoundsException exc) { System.out.println(“Format is: DuplicateFile ”); return; } try //copy the source file to the drain file { do { i = source.read( ); if (i != -1) drain.write(i); } while (i != -1); } catch(IOException exc) { System.out.println(“A File Error Has Occurred…Run For Cover”); } source.close( ); drain.close( ); } //end main } //end DuplicateFile Reading and Writing Files Using Reader/Writer Classes

//Program to read characters from the keyboard using a BufferedReader //Format: java ReadCharsFromKeyboard import java.io.; class ReadCharsFromKeyboard { public static void main(String args[ ]) throws IOException { char keyboardinput; BufferedReader my_reader = new BufferedReader(new InputStreamReader(System.in)); System.out.println(“Enter characters. Use $ to quit.”); // read the input from the keyboard do { keyboardinput = (char) my_reader.read( ); System.out.println(keyboardinput); } while (keyboardinput != “$”); } //end main } //end ReadCharsFromKeyboard To read strings rather than single characters from the keyboard make use of the version of the readline( ) method which is available in the BufferedReader class. This will return a String object which contains characters that were read from the keyboard. The example below will simply read in strings that you input from the keyboard and display them on the screen and repeat this process until the string that you enter from the keyboard is “quit”. //Program to read strings from the keyboard until the string = “quit” is entered //Format: java ReadSomeStrings import java.io.; class ReadSomeStrings { public static void main(String args [ ]) throws IOException { BufferedReader my_reader = new BufferedReader( new InputStreamReader(System.in)); String a_string; System.out.println(“Enter some strings please…enter ‘quit’ to end”); do { a_string = my_reader.readLine( ); System.out.println(a_string); } while (!a_string.equals(“quit”)); } //end main } //end ReadSomeStrings Using Type Wrappers To Convert Numeric Strings

The Java println( ) method which you have used many times provides a simple technique to output various types of data to the screen, including the numeric values of the primitive types int and double. The println( ) method automatically converts these numeric values into human readable form (i.e., text characters). There is no equivalent input method that will read and convert strings which contain numeric values into their internal binary formats. You can’t enter a string such as “345” from the keyboard and have this automatically converted into its corresponding binary form able to be stored in a variable of type int. To be able to do this you need to utilize Java’s type wrappers. Type wrappers are classes that encapsulate (wrap) the simple types. Type wrappers are required because the simple types are not objects in Java which in some cases severely limits their use. For example, a simple type cannot be passed by reference. Thus, there is a wrapper class which corresponds to each of the simple types. These type wrapper classes are Double, Long, Integer, Float, Byte, Short, Character, and Boolean. Each of these classes contains a number of methods that allow the simple types to be integrated into the object hierarchy of Java. A side benefit is that the numeric wrapper classes define methods which convert a numeric string into its binary equivalent. These wrapper classes are listed below along with the conversion method used by each class. Wrapper Conversion Method Double static double parseDouble(String str) throws NumberFormatException Long static long parseLong(String str) throws NumberFormatException Integer static int parseInt(String str) throws NumberFormatException Float static float parseFloat(String str) throws NumberFormatException Byte static byte parseByte(String str) throws NumberFormatException Short static short parseShort(String str) throws NumberFormatException The parsing methods provide an easy technique to convert the numeric values, read as strings from the keyboard or a text file, into its proper internal format. The following program illustrates both the parseInt( ) and parseDouble( ) methods. This program simply averages a list of numbers that is input by the user from the keyboard assuming that the first number is the number of numbers in the list.

stream reader and your program. In this case you do not need a buffered reader. The technique is similar to that of the buffered reader classes in that you need to create a stream tokenizer which is connected to an input stream reader. To do this you use an expression that creates an instance of the StreamTokenizer class for the input stream reader to which it will be linked. StreamTokenizer instances are calle either stream tokenizers or more commonly tokenizers. This is done in the following manner: StreamTokenizer my_tokens = new StreamTokenizer(my_reader); Just as we did before, my_reader will need to be an input stream reader instance which is linked to a particular input stream. If the input stream is to be the keyboard then the instance of my_reader would be instantiated with the following: InputStreamReader my_reader = new InputStreamReader(System.in); StreamTokenizer my_tokens = new StreamTokenizer (my_reader); If the input stream is to be a file then the instance of my_reader would need to be linked to a stream which is connected with a file. This is done as follows: FileInputStream a_stream = new FileInputStream(); InputStreamReader my_reader = new InputStreamReader(a_stream); StreamTokenizer my_tokens = new StreamTokenizer(my_reader); In this case, can be the complete path name of file which is to be used for the input. Tokenizers treat white space in the stream as delimiters which separate the character sequence into tokens. Thus, a file containing the following characters is viewed as a stream of nine tokens divided by spaces and line- terminating characters: 3 5 6 4 9 10 5 4 1 You can conceptualize a tokenizer as a machine that steps through a stream of tokens. The nextToken( ) method moves the machine from one token to the next. Let’s assume that we have instantiated an instance of a stream tokenizer to read from an input file (as we did above) and we call the nextToken( ) method:

my_tokens.nextToken( ) The first time that Java executes a call to the nextToken( ) method, given that the first token is a number, the value of that token is assigned to the nval instance variable in the tokenizer. If we use the sample file shown above, then after this first call executes, my_tokens.nval will be 3. After Java executes the second call to nextToken( ) the second token is assigned to nval and this time my_tokens.nval will be 5. The value stored in the nval instance variable is always a double, even if what you see in the input file is an integer. The rationale behind this is that you can always cast a double into any other type. Therefore, if your file contains integer values, and you want to work with int values, you will need to cast the number obtained from the nval instance variable, such as: (int) my_tokens.nval. As the token machine moves down a stream of tokens, eventually it will reach the end of the token stream. At that point the nextToken( ) method will return the special value which is equal to the value assignes to the TT_EOF instance variable of the tokenizer (TT is an acronym for Token Type). Thus, when nextToken( ) returns a value equal to the TT_EOF instance variable, there are no more tokens to be read. This means that you can read and process all the integer tokens in a token stream with a simple while loop: while (my_tokens.nextToken( ) != my_tokens.TT_EOF) { … …(int) my_tokens.nval … … } The following example will produce a series of int values from the information in a file and simply display them one value to a line. What you actually do with these values will vary depending upon your application. //Program to illustrate stream tokenizer. Reads a file and prints its contents. //Format: java tokendemo import java.io.*; public class tokendemo { public static void main (String args[ ]) throws IOException

of the examples above, I included some exception handling so that you could see a little bit about how errors are handled in Java. The try/catch blocks are Java’s technique for dealing with run-time errors. I hope that this did not cause confusion in understanding how the I/O in Java is handled, if it does you can simply removed the exception handling code to make the I/O more easily understood. I may decide to add one more set of notes that deal with exception handling in Java, if so this would be the fifth and final set of Java notes for the course. //Program to illustrate stream tokenizer with strings and numbers in the input file. // In this case, strings are ignored and numbers are printed. //Format: java anothertokendemo import java.io.*; public class anothertokendemo { public static void main (String args[ ]) throws IOException { FileInputStream a_stream = new FileInputStream(“my_input.data”); InputStreamReader my_reader = new InputStreamReader(a_stream); StreamTokenizer my_tokens = new StreamTokenizer(my_reader); int next = 0; while (( next = my_tokens.nextToken( )) != my_tokens.TT_EOF) { switch (next) { case my_tokens.TT_WORD: break; case my_tokens.TT_NUMBER: int coef = (int) my_tokens.nval; my_tokens.nextToken( ); int exp = (int) my_tokens.nval; System.out.println(“Coefficient: “ + coef + “Exponent: “+ exp); break; } } a_stream.close( ); } }