Buffered Readers and Writers

Input and output can be time-consuming operations. It's often quicker to read or write text in large chunks rather than in many separate smaller pieces, even when you only process the text in the smaller pieces. The java.io.BufferedReader and java.io.BufferedWriter classes provide internal character buffers. Text that's written to a buffered writer is stored in the internal buffer and only written to the underlying writer when the buffer fills up or is flushed. Likewise, reading text from a buffered reader may cause more characters to be read than were requested; the extra characters are stored in an internal buffer. Future reads first access characters from the internal buffer and only access the underlying reader when the buffer is emptied.

Even if the underlying stream is buffered, it still pays to buffer the reader or writer too. Many character conversions can be done more quickly in blocks than they can on individual characters, irrespective of I/O speed. That is, for maximum performance use a BufferedReader and a BufferedInputStream or a BufferedWriter and a BufferedOutputStream.

 

20.9.1. Buffering Writes

The java.io.BufferedWriter class is a subclass of java.io.Writer that you chain to another Writer class to buffer characters. This allows more efficient writing of text.

public class BufferedWriter extends Writer

There are two constructors. One has a default buffer size (8192 characters); the other lets you specify the buffer size:

public BufferedWriter(Writer out)
public BufferedWriter(Writer out, int size)

For example:

BufferedWriter bw = new BufferedWriter(new FileWriter("37.html"));

BufferedWriter overrides most of its superclass's methods, but all changes are purely internal. write( ), flush( ), close( ), etc. are all used exactly as they are for any writer object.

There is one new method in this class, newLine( ). This method writes a platform-dependent line terminator string: on Unix, on the Mac, on Windows. The value of this string is taken from the system property line.separator.

public String newLine( ) throws IOException

Do not use the newLine( ) method if you're writing network code such as an HTTP server. Instead, explicitly write the carriage return/linefeed pair. Most network protocols specify a line separator, regardless of host-platform conventions.

Example 20-3 is a revised version of Example 20-1 that uses a BufferedWriter to increase efficiency and handle platform-dependent line separators.

Example 20-3. BufferedUnicodeTable

import java.io.*;
public class BufferedBMPTable {
 public static void main(String[] args) throws IOException {
 // Use platform default with a fallback to Latin-1 if necessary
 String encoding = System.getProperty("file.encoding", "ISO-8859-1");
 String lineSeparator = System.getProperty("line.separator", "
");
 OutputStream target = System.out;
 if (args.length > 0) target = new FileOutputStream(args[0]);
 if (args.length > 1) encoding = args[1];
 BufferedWriter out = null;
 try {
 out = new BufferedWriter(new OutputStreamWriter(target, encoding));
 }
 catch (UnsupportedEncodingException ex) { // platform default encoding
 out = new BufferedWriter(new OutputStreamWriter(target));
 }
 try {
 for (int i = Character.MIN_VALUE; i <= Character.MAX_VALUE; i++) {
 if (!Character.isDefined(i)) continue;
 char c = (char) i;
 if (Character.isHighSurrogate(c) || Character.isLowSurrogate(c)) continue;
 out.write(i + ":	" + c);
 out.newLine( );
 }
 }
 finally {
 out.close( );
 }
 }
}

This is actually not the fastest you can go. BufferedWriter is internally synchronized. Each call to one of its methods is atomic. If two threads try to write onto the same BufferedWriter at the same time, one of them blocks. This prevents the threads from corrupting the data. However, this synchronization has a performance cost, even when only one thread has access to the writer. You can often improve performance by replacing the stock BufferedWriter from java.io with an unsynchronized version such as shown in Example 20-4. When you don't need to worry about synchronization, this version can increase speed by 30-50%, though as always exact performance gains are likely to vary from one VM to the next.

Example 20-4. UnsynchronizedBufferedWriter

package com.elharo.io;
import java.io.*;
public class UnsynchronizedBufferedWriter extends Writer {
 private final static int CAPACITY = 8192;
 private char[] buffer = new char[CAPACITY];
 private int position = 0;
 private Writer out;
 private boolean closed = false;
 public UnsynchronizedBufferedWriter(Writer out) {
 this.out = out;
 }
 public void write(char[] text, int offset, int length) throws IOException {
 checkClosed( );
 while (length > 0) {
 int n = Math.min(CAPACITY - position, length);
 System.arraycopy(text, offset, buffer, position, n);
 position += n;
 offset += n;
 length -= n;
 if (position >= CAPACITY) flushInternal( );
 }
 }
 public void write(String s) throws IOException {
 write(s, 0, s.length( ));
 }
 public void write(String s, int offset, int length) throws IOException {
 checkClosed( );
 while (length > 0) {
 int n = Math.min(CAPACITY - position, length);
 s.getChars(offset, offset + n, buffer, position);
 position += n;
 offset += n;
 length -= n;
 if (position >= CAPACITY) flushInternal( );
 }
 }
 public void write(int c) throws IOException {
 checkClosed( );
 if (position >= CAPACITY) flushInternal( );
 buffer[position] = (char) c;
 position++;
 }
 public void flush( ) throws IOException {
 flushInternal( );
 out.flush( );
 }
 private void flushInternal( ) throws IOException {
 if (position != 0) {
 out.write(buffer, 0, position);
 position = 0;
 }
 }
 public void close( ) throws IOException {
 closed = true;
 this.flush( );
 out.close( );
 }
 private void checkClosed( ) throws IOException {
 if (closed) throw new IOException("Writer is closed");
 }
}

All characters are first written into an internal byte array of length 8192. Only when that buffer fills up is it flushed to the underlying writer. The java.io.BufferedWriter class is organized very much like this, except that it also has a number of synchronized blocks to permit threadsafe usage.

20.9.2. Buffering Reads

BufferedReader is a subclass of Reader that is chained to another Reader class to buffer input. This allows more efficient reading of characters and lines.

public class BufferedReader extends Reader

For example:

BufferedReader br = new BufferedReader(new FileReader("37.html"));

There are two constructors. One has a default buffer size (8192 characters); the other requires the programmer to specify the buffer size:

public BufferedReader(Reader in, int buffer_size)
public BufferedReader(Reader in)

In a BufferedReader, the two multicharacter read( ) methods try to completely fill the specified array or subarray of text by reading repeatedly from the underlying reader. They return only when the requested number of characters have been read, the end of the data is reached, or the underlying reader would block. This is not the case for most readers which attempt only one read from the underlying data source before returning.

BufferedReader does support marking and resetting, at least up to the length of the buffer. Another reason to use a BufferedReader is to enable marking and resetting on a reader that otherwise wouldn't support it, such as an InputStreamReader.

Besides buffering, BufferedReader is notable for its readLine( ) method that allows you to read text a line at a time. This replaces the common but deprecated readLine( ) method in DataInputStream.

public String readLine( ) throws IOException

This method returns a string that contains a line of text from a text file. , , and are assumed to be line breaks and are not included in the returned string. This method is often used when reading user input from System.in since most platforms only send the user's input to the running program after the user has typed a full line (that is, hit the Enter key).

readLine( ) can hang if the last character of the stream is not a carriage return or a linefeed and the sender does not close the stream. This problem tends to arise on network connections where the client or server keeps a socket open for a response after sending its data. For this reason, readLine( ) should not be used in network programming.

Example 20-5 uses a BufferedReader and readLine( ) to read all files named on the command line, line by line, and copy them to System.out. In essence it implements the Unix cat or the DOS type utility.

Example 20-5. The cat program

import java.io.*;
class Cat {
 public static void main (String[] args) {
 String thisLine;
 for (int i=0; i < args.length; i++) {
 try {
 BufferedReader br = new BufferedReader(new FileReader(args[i]));
 while ((thisLine = br.readLine( )) != null) {
 System.out.println(thisLine);
 } // end while
 } // end try
 catch (IOException ex) {System.err.println(ex);}
 } // end for
 } // end main
}

20.9.3. Line Numbering

LineNumberReader is a subclass of BufferedReader that keeps track of which line it's currently reading. It also has methods to get and set the line number. This class replaces the deprecated LineNumberInputStream class.

public class LineNumberReader extends BufferedReader

This class has two constructors. Both chain this reader to an underlying reader; the second also sets the size of the buffer.

public LineNumberReader(Reader in)
public LineNumberReader(Reader in, int size)

LineNumberReader has all the methods of BufferedReader, including readLine( ). These are overridden to keep track of the line number. The behavior of these methods is not changed.

LineNumberReader also introduces two methods for inspecting and changing the line number:

public int getLineNumber( )
public void setLineNumber(int lineNumber)

The setLineNumber( ) method does not change the line that you're reading in the file. It just changes the value getLineNumber( ) returns. For example, it would allow you to start counting from -5 if you knew there were six lines of header data, and you wanted line 1 to be the first line of the body text.

Example 20-6 uses a LineNumberReader and readLine( ) to read all files named on the command line, line by line, and copy them to System.out, prefixing each line with its line number.

Example 20-6. The LineCat Program

import java.io.*;
class LineCat {
 public static void main (String[] args) {
 String thisLine;
 for (int i=0; i < args.length; i++) {
 try {
 LineNumberReader br = new LineNumberReader(new FileReader(args[i]));
 while ((thisLine = br.readLine( )) != null) {
 System.out.println(br.getLineNumber( ) + ": " + thisLine);
 } // end while
 } // end try
 catch (IOException ex) {System.err.println(ex);}
 } // end for
 } // end main
}

Basic I/O

Introducing I/O

Output Streams

Input Streams

Data Sources

File Streams

Network Streams

Filter Streams

Filter Streams

Print Streams

Data Streams

Streams in Memory

Compressing Streams

JAR Archives

Cryptographic Streams

Object Serialization

New I/O

Buffers

Channels

Nonblocking I/O

The File System

Working with Files

File Dialogs and Choosers

Text

Character Sets and Unicode

Readers and Writers

Formatted I/O with java.text

Devices

The Java Communications API

USB

The J2ME Generic Connection Framework

Bluetooth

Character Sets



Java I/O
Java I/O
ISBN: 0596527500
EAN: 2147483647
Year: 2004
Pages: 244

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