Lesson 13.4 Character Streams
This section will teach you about Java's new Reader and Writer classes. These classes were introduced in Java 1.1 to facilitate dealing with Unicode character I/O.
In Java, the char data type is a 16-bit Unicode character, not an ASCII byte. The original I/O classes, InputStream and OutputStream, however, were designed to read and write bytes.
Sun quickly discovered that applying these byte-oriented streams to the task of reading and writing Unicode was more trouble than it was worth, and so they introduced a parallel set of classes designed just for this task.
Unicode and Windows |
| Sun is not the only company to find itself dashed against the Unicode cliffs. Microsoft has invested a tremendous amount of time and energy trying to retrofit the Windows application programming interfaces to use Unicode characters, and have come up with pretty much the same solution. If you want to read and write Unicode characters under the Windows API, you must use the new set of parallel functions provided for that purpose. |
Reader, Writer
The Reader and Writer classes act just like InputStream and OutputStream. They are abstract classes at the root of a large hierarchy of classes designed to work with text.
When working with text, rather than using a FileInputStream object, you should construct a FileReader; rather than using a BufferedInputStream, you use a BufferedReader.
Let's see how that works by writing an application that reads lines of text and writes them to a second file.
Your application, called CopyFile.java, will take two arguments like this:
| java CopyFile sourceFile destinationFile |
The steps you need to follow are:
- Construct a FileReader object using a file name, and a FileWriter object using a file name.
- Wrap the FileReader in a BufferedReader., and the FileWriter in a BufferedWriter.
- Use the BufferedReader's readLine() method, and the BufferedWriter's write() and newline() methods.
Opening Readers and Writers
The code to open a FileReader and a BufferedReader, or a FileWriter and a BufferedWriter, is not much different than what you'd employ to open a FileInputStream and a BufferedInputStream:
FileReader fr = null;
BufferedReader br = null;
FileWriter fw = null;
BufferedWriter bw = null;
try
{
fr = new FileReader(args[0]);
br = new BufferedReader(fr);
fw = new FileWriter(args[1]);
bw = new BufferedWriter(fw);
}
catch (Exception e)
{
System.err.println("Couldn't open file");
} |
Reading Lines
Once you've opened your files, you can use the BufferedReader readLine() method to get your data.
The readLine() method:
- returns a single line of text as a String.
- discards the newline character [or characters] at the end of the line.
- returns null if it is unable to read a line, because of end-of-file, for instance.
Using this info, you can construct a loop that reads all of the text in a file, line-by-line, like this:
String s;
while ((s = br.readLine()) != null)
; // Do stuff here |
Writing Lines
To write the lines of text out to the BufferedWriter, you use the three-argument version of write(). The three arguments are:
- The String to write.
- The starting offset. Pass 0 to start at the beginning.
- The number of characters to write. Pass s.length() to write all of the characters in the String.
After you write your String, you need to add the newline character[s] that readLine() stripped off.
Rather than appending a literal "\n", you should use the newLine() method. That's because different platforms use different line endings. Unix uses "\n", DOS uses "\r\n", and the Mac uses "\r". By calling newLine() you ensure that the correct ending is written for your platform.
Here is the completed read-write loop from CopyFile.java:
String s;
while ((s = br.readLine()) != null)
{
bw.write(s, 0, s.length());
bw.newLine();
} |
Closing Your Files
After you've copied all of the data from your BufferedReader to your BufferedWriter, you have to close your files.
When you do this, make sure you do it in the reverse order in which the files were opened. Specifically, make sure you don't close the underlying FileWriter before closing the BufferedWriter.
When you close the BufferedWriter, it sends the data it's been inventorying on to the FileWriter for deposit on disk. If you accidentally close your FileWriter before your BufferedWriter, the BufferedWriter has no one to hand the data off to and you end up with a 0-byte file.
Here's the code to close the files in CopyFile.java:
finally
{
try
{
br.close();
fr.close();
bw.close();
fw.close();
}
catch (Exception e) { e.printStackTrace(); }
} |
Binary-Text Conversion
When you need to display binary data, floats and ints, for instance, the data first has to be converted to a textual form.
One way to do that is to use String concatenation to convert your binary values to Strings, and then print the Strings using BufferedWriter. Because this is such a common task, however, Java has a class specially designed to perform it: the PrintWriter class.
The PrinterWriter class is a "wrapper" class like BufferedWriter. You first must create a regular Writer, such as FileWriter, and then pass the Writer on to the PrintWriter constructor like this:
FileWriter fw = null;
PrintWriter pw = null;
try
{
fw = new FileWriter(args[0]);
pw = new PrintWriter(fw);
} |
The PrintWriter class has a second constructor that allows you to convert an OutputStream object to a PrintWriter. This is useful for converting the System.out object, [which is a PrintStream object] to a PrintWriter.
PrintWriter pw =
new PrintWriter(System.out, true); |
The most commonly used methods in the PrintWriter class are the familiar print() and println(). Unlike most of the other Writer methods, the PrintWriter class does not throw exceptions when errors occur in these methods; if you suspect something may be going wrong, then you must call your PrintWriter's checkError() method. (The PrinterWriter write() method still throws an exception, of course.)
Something to Talk About
The Reader and Writer classes are designed to work with 2-byte Unicode characters. Do the Writer classes actually write 2-byte characters to disk? How can you tell?
Please continue to the next section of this lesson.
|