【问题标题】:Is Java's EOFException exceptional?Java 的 EOFException 异常吗?
【发布时间】:2010-07-27 15:59:47
【问题描述】:

使用异常来指示已到达文件末尾似乎很脏。我们读取的每个文件都有一个结尾,因此它看起来并不异常或意外。此外,我不喜欢对我的程序的非异常流程使用异常。

我说的是使用java.io.EOFException 来表示数据输入流的结束:

想象一个由以下消息组成的文件...

----------------- ------------------
- 2-byte LENGTH - - N-byte PAYLOAD - , where N = LENGTH;
----------------- ------------------

...并使用 DataInputStream 读取此文件:

DataInputStream in = new DataInputStream(...);

...

try {
    while (true) {
        short length = in.readShort();
        byte[] b = new byte[length];
        in.readFully(b);
    }
} catch (EOFException e) { }

...

在此示例中,调用 in.readShort() 会引发 EOFException。我是否应该计算出文件中的字节数,并准确读取该字节数(由total -= length 确定为零),然后无一例外地退出 while 循环?我正在寻找最佳实践。

我应该这样做吗?

long total = file.length();
while (total > 0) {
    short length = in.readShort();
    total -= length;
    byte[] b = new byte[length];
    in.readFully(b);
}

API 规范指定 EOFException 表示在输入期间意外到达了文件结尾或流结尾。但它也被数据输入流用来表示流结束。

当出现异常时我该怎么办?

【问题讨论】:

  • 应该是total -= length+2

标签: java exception io inputstream eofexception


【解决方案1】:

DataInput.readFully方法参考API specification

 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.

所以这个想法是它要么读取 b.length 字节的数据,要么如果它不能这样做,你会得到一个错误,要么是因为 I/O 错误,要么是在 b.length 字节之前到达文件结尾被阅读。

因此,在调用DataInput.readFully 之前,您应该知道要读取多少字节。如果超出文件末尾,则被认为是异常行为,因此是您获得异常的原因。

【讨论】:

  • 在我的示例中,EOFException 由in.readShort(); 引发。我应该做其他事情吗,比如弄清楚文件的大小并准确读取那个字节数?
  • readShort 的工作方式相同。 java.sun.com/j2se/1.4.2/docs/api/java/io/… 也就是说,您应该知道可以从文件中读取一个short,如果您尝试从文件中读取并且没有一个short,那么您将得到一个异常。为避免此类问题,您始终可以使用 readAllBytes (openjdk.java.net/projects/nio/javadoc/java/io/…) 如果您希望文件数据为字符串,也可以使用 readAllLines。
  • 由于我不希望在最后一次调用时 readShort(当文件被完全读取时),我该怎么办?
  • 如果您非常反对使用异常,您可以提前检查文件的长度,跟踪您已读取的字节数,并将其用作您的退出条件 while环形。但是当然你仍然需要捕获 EOFException 以防 readFully() 抛出它(如果输入文件以某种方式损坏),那么有什么意义呢?
  • 如果不知道你的文件结构,我很难说。您是否正在阅读固定格式的文件(即 field1 是 10 字节,field2 是 20 字节,field3 是 14 字节等)?您正在阅读 CSV 文件吗?这真的取决于你的输入。如果您只是读取一个充满数字的文件并且您不知道有多少,那么除非您先读取所有文件文本然后处理字符串,否则很难避免异常。
【解决方案2】:

您提到了通常给出的建议,即异常应仅用于发出异常事件的信号。这个建议的问题在于它用另一个不确定性(“何时抛出异常”)替换了另一个不确定性(“什么是异常的”)。

另一个更好的答案是,当且仅当替代方法是无法建立后置条件或保持不变量时,您应该抛出异常。

对输入流的读操作的后置条件是一个值已被成功读取。显然,如果我们处于 EOF,则无法读取任何值。所以必须抛出某种类型的异常。调用代码想要停止读取,因此它必须是一种独特的异常类型(不是一般的 IOException)。因此需要一个特定的 EOFException。

另一种方法是读取操作没有成功读取作为后置条件。这就是 C (POSIX) API 所做的。实际上,每个读取操作实际上都是一次尝试读取操作。然后,阅读器代码需要检查状态代码以查看读取尝试是否实际成功。这种代码风格难以理解、冗长且容易出错。使用异常而不是状态码的动机之一是避免这些困难。

【讨论】:

    【解决方案3】:

    顾名思义,readFully 用于当您希望完全读取一些已知长度的字节时。如果之前达到 EOF,那是出乎意料的,然后在概念上抛出异常是正确的。如果您想要read 语义,请使用read 方法。

    【讨论】:

    • 那在我的例子中应该怎么做呢?
    猜你喜欢
    • 1970-01-01
    • 2011-02-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-11-12
    • 1970-01-01
    • 1970-01-01
    • 2011-06-30
    相关资源
    最近更新 更多