Sequential Character Stream File IO Using Readers Writers


Sequential Byte Stream File I/O Using Input- & OutputStreams

InputStreams and OutputStreams are used to perform sequential byte-oriented I/O. Refer to figure 18-1 again and study the InputStream and OutputStream inheritance hierarchy. The InputStream and OutputStream classes are abstract so you must use one or more of their subclasses, alone or in combination with each other, to perform file I/O operations. When you perform file I/O operations with InputStreams and OutputStreams you read and write data sequentially. You cannot maneuver around the file like you can with the RandomAccessFile class.

I will discuss the OutputStream classes first and show you how to write byte streams and objects to a file. I will then show you how to read those files using InputStream classes.

OutputStreams

The FileOutputStream, BufferedOutputStream, DataOutputStream, ObjectOutputStream, and PrintStream classes are used to write byte streams, primitive types, objects, and text to disk. The use of each class is discussed and demonstrated below.

FileOutputStream

You can use the FileOutputStream class to perform sequential byte output to a file. The FileOutputStream user interface is minimal. In addition to five overloaded constructors it provides three overloaded versions of a write() method you can use to write a single byte, an array of bytes, or a segment of an array of bytes to a file. It provides a close() method which should be called to close the file when you have finished writing data. Its two other methods getChannel() and getFD() will not be discussed here. (The getChannel() and getFD() methods are used in conjunction with classes belonging to the java.nio package.)

Example 18.3 shows the OutputStream class in action.

Example 18.3: FOS_TesterApp.java

image from book
 1     import java.io.*; 2 3     public class FOS_TesterApp { 4       public static void main(String[] args){ 5         FileOutputStream fos; 6         File file; 7         try{ 8          fos = new FileOutputStream("Test.txt", true); 9          if(args.length != 0){ 10           fos.write(args[0].getBytes()); 11          }else{ 12            fos.write(("Ohhhh do you love Java like I love Java?").getBytes()); 13            } 14              fos.close(); 15              file = new File("test.txt"); 16              System.out.println("The length of the file Text.txt in bytes is: " + file.length()); 17        }catch(Exception ignored){ } 18      } // end main 19    } // end class
image from book

Referring to example 18.3 — the program will write to disk any text entered on the command line (up to the first space) when the program is executed. If no text is entered on the command line then it writes the phrase “Ohhhh do you love Java like I love Java?” to disk. The FileOutputStream object is created on line 8. The arguments to the constructor include the name of the file to open and the boolean literal true which enables file appending.

Notice how the String.getBytes() method is called on the String literal on line 12. The close() method is called on line 14 to close the file. After the file is closed a File object is created and used to get the length of the text.txt file. If you run this program repeatedly without deleting the file in between runs it will append the new text to the end of the file. Figure 18-4 shows the results of running this program several times.

image from book
Figure 18-4: Results of Running Example 18.3

Referring to figure 18-4 — notice how the file length grows each time the application executes. Figure 18-5 shows how the file looks when opened in a text editor. Notice how each successive write appended text to the end of the file.

image from book
Figure 18-5: Contents of test.txt After Executing Example 18.3 Six Times

If you want to overwrite the file you can call the constructor on line 8 with the boolean literal false or use the FileOutputStream(String name) version of the constructor which, by default, sets the append property to false.

BufferedOutputStream

When using the FileOutputStream class to perform file output, each time the write() method is called a disk write occurs. In situations where you anticipate a significant amount of disk writes you may want to improve your program’s I/O efficiency by using the BufferedOutputStream class. The BufferedOutputStream class stores data writes in an internal memory array (buffer). The data is actually written to disk when one of two events occur: 1) when the buffer fills up, or 2) when the flush() method is called to force a write.

To use a BufferedOutputStream object you first create the FileOutputStream object then use it to create the BufferedOutputStream object. You then call the write() method on the BufferedOutputStream object. Example 18.4 shows the BufferedOutputStream class in action.

Example 18.4: BOS_TesterApp.java

image from book
 1     import java.io.*; 2 3     public class BOS_TesterApp { 4       public static void main(String[] args){ 5         FileOutputStream fos; 6         BufferedOutputStream bos; 7         File file; 8         try{ 9             file = new File("test.txt"); 10            fos = new FileOutputStream(file); 11            bos = new BufferedOutputStream(fos); // default buffer size is 512 bytes 12            while(file.length() == 0){ 13            if(args.length != 0){ 14                bos.write(args[0].getBytes()); 15            }else{ 16                 bos.write(("Ohhhh do you love Java like I love Java?").getBytes()); 17            } 18             System.out.println("The length of the file Text.txt in bytes is: " + file.length()); 19            } 20             fos.close(); 21         }catch(Exception ignored){ } 22       } // end main 23     } // end class
image from book

Referring to example 18.4 — this program looks similar to example 18.3 but with a few important changes. First, a File object is created on line 9 and is used to create the FileOutputStream on line 10. The fos reference is passed as an argument to the BufferedOutputStream constructor on line 11 to create the BufferedOutputStream object. A while loop is used starting on line 12 to repeatedly write to the BufferedOutputStream buffer. The while loop will repeat until data is actually written to the file. (i.e., The buffer fills up at which time its contents are written to the disk in one stroke.) Figure 18-6 shows the results of running this program. Figure 18-7 shows the contents of the image from book test.txt file after the application execution completes.

image from book
Figure 18-6: Results of Running Example 18.4

image from book
Figure 18-7: Contents of test.txt File After Executing Example 18.4

The default buffer size for a BufferedOutputStream object is set to 512 bytes. If the next write operation performed on the BufferedOutputStream object will exceed the buffer size then the buffer’s contents are written to disk automatically. You can force the write by calling the flush() method if required. You can also set the internal buffer size via the constructor when you create a BufferedOutputStream object.

DataOutputStream

The DataOutputStream is used to write streams of bytes as well as primitive type byte values to a file. If it is used in concert with a FileOutputStream object it provides no buffering and every write will be immediately written to disk. To provide write buffering use the DataOutputStream in conjunction with a BufferedOutputStream object. Example 18.5 shows the DataOutputStream class in action.

Example 18.5: DOS_TesterApp.java

image from book
 1     import java.io.*; 2 3     public class DOS_TesterApp { 4       public static void main(String[] args){ 5         FileOutputStream fos; 6         BufferedOutputStream bos; 7         DataOutputStream dos; 8         File file; 9         try{ 10            file = new File("test.txt"); 11            fos = new FileOutputStream(file); 12            bos = new BufferedOutputStream(fos); // default buffer size is 512 bytes 13            dos = new DataOutputStream(bos); 14            while(file.length() == 0){ 15             if(args.length != 0){ 16                dos.writeBytes(args[0]); 17             }else{ 18                 dos.writeBytes("Ohhhh do you love Java like I love Java?"); 19             } 20         System.out.println("The length of the file Text.txt in bytes is: " + file.length()); 21         } 22         fos.close(); 23         }catch(Exception ignored){ } 24       } // end main 25     } // end class
image from book

Referring to example 18.5 — the File object is created on line 10. The file reference is used to create the FileOutputStream object on line 11. The fos reference is then used to create the BufferedOutputStream object on line 12, and the bos reference is used to create the DataOutputStream object on line 13. There is sort of a rhythm to it. First create the File, then the FileOutputStream, then the BufferedOutputStream, then the DataOutputStream. (The File object is optional.)

The dos reference is then used in the program to perform the writes to the file as is done on lines 16 and 18 with the writeBytes() method. The writeBytes() method takes a String argument, converts it to a stream of bytes, and then writes it to disk.

Figure 18-8 shows the results of running this program. Notice that the writeBytes() method behaves somewhat differently than the write() method in that it actually waits until the buffer is completely full and then flushes its contents to the file. Figure 18-9 shows the contents of image from book test.txt when the program competes execution.

image from book
Figure 18-8: Results of Running Example 18.5

image from book
Figure 18-9: Contents of test.txt After Running Example 18.5

ObjectOutputStream

The ObjectOutputStream class can be used in place of the DataOutputStream to perform sequential byte writes to a file. Additionally, you can use ObjectOutputStream to write primitive types and serialized objects to a file as well. To use an ObjectOutputStream object in your program you first create a FileOutputStream, then, optionally, a BufferedOutputStream, and finally the ObjectOutputStream. Objects written to disk must implement the Serializable or the Externalizable interface.

I will demonstrate the functionality of the ObjectOutputStream class by showing you how to write a collection of Person objects to a file. Example 18.6 gives the code for the simplified Person class.

Example 18.6: Person.java

image from book
 1     import java.io.*; 2 3     public class Person implements Serializable { 4 5         private String _f_name; 6         private String _m_name; 7         private String _l_name; 8         private String _gender; 9 10        public Person(String f_name, String m_name, String l_name, String gender){ 11            _f_name = f_name; 12            _m_name = m_name; 13            _l_name = l_name; 14            _gender = gender; 15        } 16 17        public String getFirstName(){ return _f_name; } 18        public String getMiddleName(){ return _m_name; } 19        public String getLastName(){ return _l_name; } 20        public String getGender(){ return _gender; } 21        public String toString(){ return _f_name + " " + _m_name + " " + _l_name + " " + _gender; } 22 23    }
image from book

This simplified Person class provides fields for a Person’s first name, middle name, last name, and gender, along with a set of related accessor methods. Example 18-7 gives the code for a program that creates a Java generic Vector<Person> and writes the entire vector to disk.

Example 18.7: OOSTesterApp.java

image from book
 1     import java.io.*; 2     import java.util.*; 3 4     public class OOSTesterApp { 5         public static void main(String[] args){ 6           Vector<Person> people_vector = new Vector<Person>(); 7           people_vector.add(new Person("Rick", "W", "Miller", "Male")); 8           people_vector.add(new Person("Steve", "J", "Jones", "Male")); 9           people_vector.add(new Person("Elwood", "S", "Crick", "Male")); 10          people_vector.add(new Person("Johnny", "Wad", "Borders", "Male")); 11          people_vector.add(new Person("Michael", "L", "Morrison", "Male")); 12 13          for(int i = 0; i<5; i++){ 14            System.out.println(people_vector.elementAt(i).toString()); 15         } 16 17          FileOutputStream fos; 18          ObjectOutputStream oos; 19 20          try{ 21              fos = new FileOutputStream("People.dat"); 22              oos = new ObjectOutputStream(fos); 23              oos.writeObject(people_vector); 24              fos.close(); 25          }catch(Exception ignored){} 26 27       }//end main 28      }//end class
image from book

Referring to example 18.7 — the Vector<Person> reference named people_vector is created on line 6. On lines 7 through 11, five Person objects are created and added to the people_vector. The for statement on line 13 simply iterates through the people_vector and calls the toString() method on each contained Person object.

On lines 17 and 18 the FileOutputStream and ObjectOutputStream references are declared. These are used in the try block beginning on line 20 to create the file and write the people_vector object to disk. Any exceptions that may be generated are ignored in this example.

Figure 18-10 shows the results of running example 18.7. Figure 18.11 shows how the contents of the People.dat file appear when viewed with a text editor.

image from book
Figure 18-10: Results of Running Example 18.7

image from book
Figure 18-11: Contents of People.dat File Viewed with Text Editor

PrintStream

The PrintStream class is used to write to file textual representations of Java primitive types. You can also use the class to write bytes, or arrays of bytes, to a file as well. To use the PrintStream class you must first create a FileOutput-Stream object. The PrintStream class offers three overloaded constructors that let you specify the output text encoding scheme and whether or not to automatically flush the buffer each time a write or a print is performed.

Example 18.8 demonstrates the use of the PrintStream class. This example uses the simplified Person class listed in example 18.6.

Example 18.8: PrintStreamTestApp.java

image from book
 1     import java.io.*; 2 3     public class PrintStreamTestApp { 4       public static void main(String[] args){ 5         try{ 6             PrintStream ps = new PrintStream(new FileOutputStream("Output.txt")); 7             ps.println("This file created with a PrintStream object!"); // print String literal 8             ps.println(343);    // print integer literal 9             ps.println(2.111);  // print float literal 10            ps.println(false);  // print boolean literal 11            ps.println(new Person("Rick", "W", "Miller", "Male")); // print Object String 12            ps.close(); 13           }catch(Exception ignored){} 14       }// end main 15     }// end class
image from book

Referring to example 18.8 — The PrintStream object is created inside the try block on line 6. Notice how the FileOutputStream object is created “on the fly” as an argument to the PrintStream constructor. You will see this shortcut technique used often in Java programming examples.

The ps reference is used to write the text forms of various objects to the image from book Output.txt file. A String literal is written on line 7, followed by an integer literal on line 8, a double literal on line 9, and a boolean literal on line 10. A Person object is written on line 11. But what actually gets written to the file? Figure 18-12 shows the contents of the image from book Output.txt file viewed with a text editor.

image from book
Figure 18-12: Contents of Output.txt File After Example 18.8 Executes

Notice how the String representation of the Person object is written to the file. This happened because the Person object supplied a toString() method, which overrides the Object.toString() method. If you want to save textual representations of your objects be sure to provide an overriding toString() method. (Note: The System.out object is a Print-Stream object.)

InputStreams

In this section I will demonstrate the use of the InputStream classes by reading and displaying the contents of the files created in the preceding sections with the OutputStream classes. The classes demonstrated in this section include the FileInputStream, BufferedInputStream, DataInputStream, and ObjectInputStream.

InputStream classes read input sequentially from the start of the file to the end of the file. You can read all the bytes in a file at one time or read various parts of the file sequentially. Your choice of InputStream class is dictated mostly by what type of data is stored in the file and how you want to read it. Technically, you can read any file type with these classes but the data contained within that file may or may not be interpreted properly. You generally need apriori knowledge of a file’s content organization to properly read and interpret a file’s data.

FileInputStream

The FileInputStream is a file-terminal class. In most situations where you want to read a byte stream file you need to create a FileInputStream object first, followed by either a BufferedInputStream, DataInputStream, or an ObectInputStream object. Example 18.9 shows a FileInputStream object being used to read the Text.txt file created with the execution of example 18.3.

Example 18.9: FIS_TesterApp.java

image from book
 1     import java.io.*; 2 3     public class FIS_TesterApp { 4       public static void main(String[] args){ 5         try{ 6            File file = new File("Test.txt"); 7            FileInputStream fis = new FileInputStream(file); 8            byte[] b = new byte[(int)file.length()]; 9            fis.read(b); 10 11           for(int i = 0; i<b.length; i++){ 12             System.out.print(b[i]);            // print each byte 13           } 14 15           System.out.println(); 16 17           for(int i = 0; i<b.length; i++){ 18             System.out.print((char)b[i]);      // cast to char then print 19           } 20 21           System.out.println(); 22 23           fis.close(); 24         }catch(Exception ignored){ } 25      } // end main 26    } // end class
image from book

Referring to example 18.9 — The File and FileInputStream objects are created on lines 6 and 7. A byte array the length of the file is created on line 8. The entire file is then read into the byte array on line 9. The for statement on line 11 prints each element of the byte array to the console as a byte. The for statement on line 17 prints each byte in the array after it has been cast to a character. Figure 18-13 shows the results of running this program.

image from book
Figure 18-13: Results of Running Example 18.9

Can you correlate each character to its ASCII code?

BufferedInputStream

The BufferedInputStream class is used to provide file read buffering. You can set the size of its internal buffer via the constructor at the time of object creation. Its default buffer size is 512 bytes. Example 18.10 demonstrates the use of a BufferedInputStream object.

Example 18.10: BIS_TesterApp.java

image from book
 1     import java.io.*; 2 3     public class BIS_TesterApp { 4       public static void main(String[] args){ 5         try{ 6             File file = new File("Test.txt"); 7             FileInputStream fis = new FileInputStream(file); 8             BufferedInputStream bis = new BufferedInputStream(fis); 9             byte[] b = new byte[(int)file.length()]; 10            bis.read(b); 11 12            for(int i = 0; i<b.length; i++){ 13              System.out.print(b[i]);           // print each byte 14            } 15 16            System.out.println(); 17 18            for(int i = 0; i<b.length; i++){ 19              System.out.print((char)b[i]);    // cast to char then print 20            } 21 22            System.out.println(); 23 24            bis.close(); 25         }catch(Exception ignored){ } 26      } // end main 27    } // end class
image from book

Referring to example 18.10 — the code for this example is similar to example 18.9 except now a BufferedInputStream object is created on line 8 and is used to conduct file input operations from that point on. The results of running this program are shown in figure 18-14. The version of the image from book Test.txt file used in this example is the one created in example 18.4.

image from book
Figure 18-14: Results of Running Example 18.10.

DataInputStream

The DataInputStream class provides numerous interface methods and is used to read bytes, arrays of bytes, primitive types, and text from a file. Example 18.11 shows how to use a DataInputStream object to read a series of Strings from a file. The file used as a source of input in this example is that created with the PrintStream class in example 18.8.

Example 18.11: DIS_TesterApp.java

image from book
 1     import java.io.*; 2 3     public class DIS_TesterApp { 4       public static void main(String[] args){ 5        try{ 6            File file = new File("Output.txt"); 7            FileInputStream fis = new FileInputStream(file); 8            DataInputStream dis = new DataInputStream(fis); 9 10           String input = null; 11           while((input = dis.readLine()) != null){  //readLine() method deprecated 12              system.out.println(input); 13            } 14           dis.close(); 15          }catch(Exception ignored){} 16      }// end main 17    }//end class
image from book

Referring to example 18.11 — the file object is created on line 6. It is used to create the FileInputStream object on line 7, which is used in turn to create the DataInputStream object on line 8. The while loop on line 11 reads a line of strings at a time until it encounters a null string.

The readLine() method used on line 11 is deprecated and its use will result in a compiler warning. Although this program works today, there is no guarantee it will work in future versions of Java. If you want to read text from a file you are advised to use a Reader class. Readers are covered later in this chapter.

Figure 18-15 shows the results of running this program.

image from book
Figure 18-15: Results of Running Example 18.11

ObjectInputStream

The ObjectInputStream class is used to read serialized objects from a file. With it you can read individual objects or collections of objects that have been serialized as part of an array or collection object. Example 18.12 gives the code for a class that reads the Vector<Person> object written to disk in example 18.6.

Example 18.12: OISTesterApp.java

image from book
 1     import java.io.*; 2     import java.util.*; 3 4     public class OISTesterApp { 5       public static void main(String[] args){ 6          Vector<Person> people_vector = null; 7 8          try{ 9              FileInputStream   fis = new FileInputStream("People.dat"); 10             ObjectInputStream ois = new ObjectInputStream(fis); 11             people_vector = (Vector<Person>) ois.readObject(); 12 13             for(int i = 0; i<5; i++){ 14              System.out.println(people_vector.elementAt(i).toString()); 15             } 16 17             ois.close(); 18         }catch(Exception e){System.out.println(e.getMessage()); } 19      } 20    }
image from book

Referring to example 18.12 — A Vector<Person> reference named people_vector is declared on line 6. The FileInputStream and ObjectInputStream objects are declared and created on lines 9 and 10. On line 11 the ois reference is used to read the People.dat file. The deserialized object must be cast to the proper type, which is, in this case, Vector<Person>. When this source file is compiled under Java version 5 it produces the warning shown in figure 18-16.

image from book
Figure 18-16: Warning Produced When Compiling Example 18.12

The reason for the warning? Deserialized objects are always problematic in that you cannot guarantee fully the type of object you are deserializing. Java version 5 is a little more vocal about the possible problems with deserializing objects. Figure 18-17 shows the results of running example 18.12.

image from book
Figure 18-17: Results of Running Example 18.12

Quick Review

Use OutputStreams and InputStreams to write and read byte-oriented stream data to and from a file to include single bytes, arrays of bytes, Java primitive types, serialized objects, and text.




Java For Artists(c) The Art, Philosophy, and Science of Object-Oriented Programming
Java For Artists: The Art, Philosophy, And Science Of Object-Oriented Programming
ISBN: 1932504052
EAN: 2147483647
Year: 2007
Pages: 452

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net