Inputting ASCII Characters and Binary Values

You choose an input stream when you want to bring bytes into your program. As with Readers, you decide where you want to read from, and choose one of the InputStream classes accordingly (see Table 17-7).

Table 17-7. Choose the InputStream class based on the source of the input

image

As before, you connect to socket and URL input streams using a method call, rather than a constructor. The method getInputStream() returns an input stream connected to the network resource, as shown in Table 17-8.

Table 17-8. Choose the getInputStream() method based on the source of the data

Read input from

Class

Method in that class to get an Input Stream

A socket

java.net.Socket

public InputStream getInputStream() throws java.io.IOException;

A URL connection

java.net.URLConnection

public InputStream getInputStream() throws java.io.IOException;

Using the constructors or method calls we can get an input stream that reads bytes from a file, a socket, a URL, a pipe, or a byte array. Once you have your input stream of whatever variety, it has certain basic methods available.

Basic InputStream methods

All InputStreams give you at least these three somewhat basic input methods:

public int read()
public int read(byte[] b)
public int read(byte[] b, int from, int len)

These read into, respectively, a single byte, an array of bytes, and a range in an array of bytes. The call will not return until some data is available to read, although it won't necessarily fill the array. The return value will be -1 if the InputStream hits end of file (EOF). This is why the single byte call returns a 32-bit int, even though it only reads a 8-bit byte. The high-order 24 bits in the return value allow you to distinguish EOF from a byte read. Those bits will be zero when a data byte is read and 0xFFFFFF when EOF is reached.

The ByteArrayInputStream allows you to take data from a byte array in your program using the read() methods rather than array indexing. There is a new package in JDK 1.4 called java.nio. This package supports methods that write out an array full of data to a stream with one statement. (See Chapter 18 for an example.)

All java.io classes with “InputStream” in their name have a few other methods, too, like close(), available(), and skip(). They are methods promised by the abstract class InputStream. Those three basic read() methods generally aren't enough, so you will wrap another class around your Input Stream. The most common wrapper is a DataInputStream class to read binary bytes. Anything written with a DataOutputStream can be read back in by a DataInputStream.

The DataInputStream class has these methods for binary input.

public class java.io.DataInputStream
         extends java.io.FilterInputStream implements java.io.DataInput {
    public DataInputStream(java.io.InputStream);
    public final int read(byte[]) throws java.io.IOException;
    public final int read(byte[], int, int) throws java.io.IOException;
    public final void readFully(byte[]) throws java.io.IOException;
    public final void readFully(byte[], int, int) throws java.io.IOException;
    public final int skipBytes(int) throws java.io.IOException;
    public final boolean readBoolean() throws java.io.IOException;
    public final byte readByte() throws java.io.IOException;
    public final int readUnsignedByte() throws java.io.IOException;
    public final short readShort() throws java.io.IOException;
    public final int readUnsignedShort() throws java.io.IOException;
    public final char readChar() throws java.io.IOException;
    public final int readInt() throws java.io.IOException;
    public final long readLong() throws java.io.IOException;
    public final float readFloat() throws java.io.IOException;
    public final double readDouble() throws java.io.IOException;
    public final String readLine() throws java.io.IOException;  // deprecated
    public final String readUTF() throws java.io.IOException;
    public static final String readUTF(java.io.DataInput) throws
                                                             java.io.IOException;
}

As you can see, there is a read method for all primitive types, e.g., readInt() to read an int. A line of 8-bit bytes can be read into a String using the readLine() method, but this has been deprecated because it lacks proper byte-to-double-byte conversion. You can also read a string that was written in the UTF-encoded format where characters are one to three bytes in length (readUTF() method). The UTF format string is preceded by a 16-bit length field, allowing your code to scoop up the right amount of data. Notice that, although there is a DataOutputStream.writeChars(), there is no DataInputStream.readChars(). You have to read chars one at a time, and you decide when you have read the entire string.

A word about IOExceptions

Let's say a few words on the subject of IOExceptions. If you look at the run-time source, you'll see that there are a dozen I/O-related exceptions. The most common I/O-related exceptions are:

FileNotFoundException

EOFException

InterruptedIOException

UTFDataFormatError

These are all subclasses of IOException. InterruptedIOException was supposed to be raised when you called the interrupt method of a thread that was blocked on I/O. It didn't work very well, and we'll look at the replacement (asynchronous I/O) in Chapter 18.

The name EOFException suggests that it is thrown whenever EOF (end of file) is encountered, and that therefore this exception might be used as the condition to break out of a loop. Unhappily, it can't always be used that way. EOFException is raised in only three classes: DataInputStream, ObjectInputStream, and RandomAccessFile (and their subclasses). The EOFException would be better named UnexpectedEOFException, as it is only raised when the programmer has asked for a fixed definite amount of input, and the end of file is reached before all the requested amount of data has been obtained.

EOFException is not a universal alert that the normal end of file has been reached. In FileInputStream or FileReader, you detect EOF by checking for the -1 return value from a read, not by trying to catch EOFException. So if you want to use an EOFException to terminate a loop, make sure that the methods you are using will throw you one. The compiler will warn you if you try to catch an exception that is not thrown.

FileNotFoundException is self-explanatory. UTFDataFormatException is thrown when the I/O library finds an inconsistency in reading some UTF data.

Dumping a file to hexadecimal example

Here is some sample code to read a file and dump its contents in hexadecimal.

Run the program with:

java  Dump  someFile

and it will create an output file based on the input filename, e.g. someFile.hex, that contains a hex dump of the input file.

import java.io.*;
import java.util.*;
import java.sql.Timestamp;
public class Dump {

    static FileInputStream myFIS = null;
    static FileOutputStream myFOS = null;
    static PrintStream myPOS = null;

    static public void main(String[] arg) {
           PrintStream e = System.err;
           try {
               myPOS = new PrintStream ( arg[0]+".hex" );
               long now = new Date().getTime();
               myPOS.printf("Hex dump of file %s %s %n", arg[0], new Timestamp(now));
               myFIS = new FileInputStream(arg[0]);

               while ( myFIS.available()>0 )
                        dump( (byte)myFIS.read() );
           } catch(IOException x) {
                      e.println("Exception: " + x.getMessage() );
           }
    }

    static private long byteCount = 0;

    static private void dump(byte b) {
           if (byteCount % 16 == 0)
                 myPOS.printf("%n%0^8x:", byteCount);
           // output a space every 4 bytes
          if (byteCount++ % 4 == 0)
                 myPOS.print("  ");

           // dump the byte as 2 hex chars
           myPOS.printf("%0^2x", b);
    }
}

You can create a printable representation of a class file by running

java  Dump  Dump.class

You can use the Dump program to look at binary files that you create with DataOutputStream. Here is the beginning of the output you get from running this program on its own class file (it will vary with different compilers).

The output will be in file Dump.class.hex:

Hex dump of file Dump.class
00000000:  ca fe ba be   00 03 00 2d   00 8e 0a 00   2f 00 45 09
00000010:  00 46 00 47   07 00 48 0a   00 03 00 49   09 00 2e 00
00000020:  4a 07 00 4b   07 00 4c 0a   00 07 00 45   0a 00 07 00
00000030:  4d 08 00 4e   0a 00 07 00   4f 0a 00 06

The first int word of this class file is 0xCAFE 0xBABE. The first word of all Java class files is 0xCAFE 0xBABE. It is what is known as a “magic number”—a special value put at a special place in a file to confer the magical ability to distinguish this kind of file from any other. It allows Java tools like the JVM to check that they have been given a class file to execute against. If you are going to put a special value there, you might as well use one that is easy to recognize and remember!

We could wrap the FileInputStream in a BufferedInputStream as described in the next section. The program works without buffering, but may be slower for large input files.The default size of a buffer in BufferedInputStream is 8 KB. That's up from 0.5 KB in JDK 1.4 but still much too small. For large inputs, you should do some measurements and see what size gives you the performance you want.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.216.27.251