Flylib.com

Books Software

 
 
 

Section 3.4. Skipping Bytes


3.4. Skipping Bytes

The skip( ) method jumps over a certain number of bytes in the input:

public long skip(long bytesToSkip) throws IOException

The argument to skip( ) is the number of bytes to skip. The return value is the number of bytes actually skipped , which may be less than bytesToSkip . -1 is returned if the end of stream is encountered . Both the argument and return value are long s, allowing skip( ) to handle extremely long input streams. Skipping is often faster than reading and discarding the data you don't want. For example, when an input stream is attached to a file, skipping bytes just requires that the position in the file be changed, whereas reading involves copying bytes from the disk into memory. For example, to skip the next 80 bytes of the input stream in :

try {
  long bytesSkipped = 0;
  long bytesToSkip = 80;
  while (bytesSkipped < bytesToSkip) {
    long n = in.skip(bytesToSkip - bytesSkipped);
    if (n == -1) break;
    bytesSkipped += n;
  }
}
catch (IOException ex) {
  System.err.println(ex);
}



3.5. Closing Input Streams

As with output streams, input streams should be closed when you're through with them to release any native resources such as file handles or network ports that the stream is holding onto. To close a stream, invoke its close( ) method:

public void close( ) throws IOException

Once you have closed an input stream, you should no longer read from it. Most attempts to do so will throw an IOException (though there are a few exceptions).

Not all streams need to be closed System.in generally does not need to be closed, for example. However, streams associated with files and network connections should always be closed when you're done with them. As with output streams, it's best to do this in a finally block to guarantee that the stream is closed, even if an exception is thrown while the stream is open . For example:

// Initialize this to null to keep the compiler from complaining
// about uninitialized variables
InputStream in = null;
try {
  URL u = new URL("http://www.msf.org/");
  in = u.openStream( );
  // Read from the stream...
}
catch (IOException ex) {
  System.err.println(ex);
}
finally {
  if (in != null) {
    try {
      in.close( );
    }
    catch (IOException ex) {
      System.err.println(ex);
    }
  }
}

If you can propagate any exceptions that are thrown, this strategy can be a little shorter and simpler. For example:

// Initialize this to null to keep the compiler from complaining
// about uninitialized variables
InputStream in = null;
try {
  URL u = new URL("http://www.msf.org/");
  in = u.openStream( );
  // Read from the stream...
}
finally {
  if (in != null) in.close( );
}



3.6. Marking and Resetting

It's often useful to be able to read a few bytes and then back up and reread them. For example, in a Java compiler, you don't know for sure whether you're reading the token < , << , or <<= until you've read one too many characters . It would be useful to be able to back up and reread the token once you know which token you've read.

Some (but not all) input streams allow you to mark a particular position in the stream and then return to it. Three methods in the java.io.InputStream class handle marking and resetting:

public void    mark(int readLimit)
public void    reset( ) 
 throws IOException
public boolean markSupported( )

The markSupported( ) method returns true if this stream supports marking and false if it doesn't. If marking is not supported, reset( ) throws an IOException and mark( ) does nothing. Assuming the stream does support marking, the mark( ) method places a bookmark at the current position in the stream. You can rewind the stream to this position later with reset( ) as long as you haven't read more than readLimit bytes. There can be only one mark in the stream at any given time. Marking a second location erases the first mark.

The only two input stream classes in java.io that always support marking are BufferedInputStream (of which System.in is an instance) and ByteArrayInputStream .

However, other input streams such as DataInputStream may support marking if they're chained to a buffered input stream first.

This is a truly bizarre design. It's almost always a bad idea to put methods in a superclass that aren't applicable to all subclasses. The proper solution to this problem would be to define a Resettable interface that declares these three methods and then have subclasses implement that interface or not as they choose. You could then tell whether marking and resetting were supported with a simple instanceof Resettable test. All I can offer by way of explanation here is that this design was invented ten years ago in Java 1.0, when not all the people working on Java were fully adept at object-oriented design.