It seems dirty to use an exception to indicate the end of a file has been reached. Every file we read has an end, so it doesn't seem exceptional or unexpected. Furthermore, I don't like using an exception for the non-exceptional flow of my program.
I'm talking about using java.io.EOFException to signal the end of a data input stream:
Imagine a file consisting of the following messages...
-------------开发者_如何学编程---- ------------------
- 2-byte LENGTH - - N-byte PAYLOAD - , where N = LENGTH;
----------------- ------------------
...and reading this file with DataInputStream:
DataInputStream in = new DataInputStream(...);
...
try {
while (true) {
short length = in.readShort();
byte[] b = new byte[length];
in.readFully(b);
}
} catch (EOFException e) { }
...
In this example, an EOFException is thrown by the call to in.readShort()
. Should I figure out the number of bytes in the file, and read exactly that number of bytes (determined by total -= length
going to zero), and exit the while-loop without an exception? I'm kind of looking for best practice.
Should I do something like this?
long total = file.length();
while (total > 0) {
short length = in.readShort();
total -= length;
byte[] b = new byte[length];
in.readFully(b);
}
The API Specification specifies that EOFException signals an end of file or end of stream has been reached unexpectedly during input. But it's also used by data input streams to signal end of stream.
What do I do when the excepted is expected?
Refer to the API specification for the DataInput.readFully
method:
This method blocks until one of the following conditions occurs:
* b.length bytes of input data are available, in which case a normal return is made.
* End of file is detected, in which case an EOFException is thrown.
* An I/O error occurs, in which case an IOException other than EOFException is thrown.
So the idea is that it's either going to read b.length bytes of data, or you get an error if it cannot do that, either because of I/O error or end of file is reached before b.length bytes can be read.
So you are expected to know how many bytes you want to read before calling DataInput.readFully
. If you go past the end of the file, that is considered abnormal behavior, and hence, is the reason you get an exception.
You allude to the commonly given advice that exceptions should be used only for signalling exceptional events. The problem with this advice is that it replaces one uncertainty ("when to throw an exception') with another ("what is exceptional").
A different, and better answer, is that you should throw an exception if, and only if, the alternative would be failure to establish a post condition or to maintain an invariant.
The post condition of a read operation on an input stream is that a value has been successfully read. Clearly, no value can be read if we are at the EOF. So an exception of some type must be thrown. The calling code would want to halt reading, so it must be a distinctive type of exception (not a general IOException). Hence a specific EOFException is necessary.
The alternative is that read operations do not have successful reading as post conditions. This is what the C (POSIX) APIs do. In effect, each read operation is really a try-to-read operation. The reader code then needs to check a status code to see whether the attempt to read was actually successful. This style of code is difficult to understand, verbose, and error prone. one of the motivations for using exceptions rather than status codes is to avoid those difficulties.
As its names suggests, readFully
is used when you expect to read fully some bunch of bytes, of known length. If EOF is reached before, that's unexpected, and then it's conceptually right to throw an exception. If you want the read
semantic, then use the read method.
精彩评论