EOFException - 如何处理?

25

我是一名初学者Java程序员,正在跟随java教程

我正在使用Java教程数据流页面中的简单Java程序,在运行时它不断显示EOFException。我想知道这是否正常,因为读取器最终必须到达文件末尾。

import java.io.*;

public class DataStreams {
    static final String dataFile = "F://Java//DataStreams//invoicedata.txt";

    static final double[] prices = { 19.99, 9.99, 15.99, 3.99, 4.99 };
    static final int[] units = { 12, 8, 13, 29, 50 };
    static final String[] descs = {
        "Java T-shirt",
        "Java Mug",
        "Duke Juggling Dolls",
        "Java Pin",
        "Java Key Chain"
    };
    public static void main(String args[]) {
        try {
            DataOutputStream out = new DataOutputStream(new BufferedOutputStream(new FileOutputStream(dataFile)));

            for (int i = 0; i < prices.length; i ++) {
                out.writeDouble(prices[i]);
                out.writeInt(units[i]);
                out.writeUTF(descs[i]);
            }

            out.close(); 

        } catch(IOException e){
            e.printStackTrace(); // used to be System.err.println();
        }

        double price;
        int unit;
        String desc;
        double total = 0.0;

        try {
            DataInputStream in = new DataInputStream(new BufferedInputStream(new FileInputStream(dataFile)));

            while (true) {
                price = in.readDouble();
                unit = in.readInt();
                desc = in.readUTF();
                System.out.format("You ordered %d" + " units of %s at $%.2f%n",
                unit, desc, price);
                total += unit * price;
            }
        } catch(IOException e) {
            e.printStackTrace(); 
        }

        System.out.format("Your total is %f.%n" , total);
    }
}

它编译通过,但输出结果为:

You ordered 12 units of Java T-shirt at $19.99
You ordered 8 units of Java Mug at $9.99
You ordered 13 units of Duke Juggling Dolls at $15.99
You ordered 29 units of Java Pin at $3.99
You ordered 50 units of Java Key Chain at $4.99
java.io.EOFException
        at java.io.DataInputStream.readFully(Unknown Source)
        at java.io.DataInputStream.readLong(Unknown Source)
        at java.io.DataInputStream.readDouble(Unknown Source)
        at DataStreams.main(DataStreams.java:39)
Your total is 892.880000.

Java教程数据流页面上可以看到:

请注意,DataStreams通过捕获EOFException来检测文件末尾的条件,而不是测试无效返回值。所有DataInput方法的实现都使用EOFException而不是返回值。

那么,这是否意味着捕获EOFException是正常的,只需捕获它而不处理它就可以了,这意味着已经到达文件末尾了吗?

如果这意味着我应该处理它,请告诉我如何处理。

编辑

根据建议,我已经通过使用in.available() > 0来修复了while循环条件。

或者,我可以不处理异常,因为这是可以接受的。


catch块中删除e.printStackTrace();将删除异常堆栈跟踪的打印。相反,您应该记录它而不是打印它。 - araknoid
available() 不是用于测试流结束的方法。你编辑中提出的解决方案是无效的。 - user207421
8个回答

25

在从文件中读取数据时,您没有终止循环。因此,在下面的行的下一次迭代中读取所有值并正确地抛出EOFException:

 price = in.readDouble();

如果您阅读文档,它会说:

抛出异常:

EOFException - 如果此输入流在读取八个字节之前到达结尾。

IOException - 流已关闭,包含的输入流不支持关闭后读取,或发生其他I/O错误。

在 while 循环中放置适当的终止条件以解决该问题,例如以下代码:

     while(in.available() > 0)  <--- if there are still bytes to read

7
当没有数据当前可供读取时,此循环将退出,而不仅是在流末尾时。 InputStream.available() 不是测试流结束的方法。请参阅 Javadoc。 - user207421

8

处理这个问题的最佳方式是使用适当的条件终止您的无限循环。

但既然您要求异常处理:

尽量使用两个catch语句。EOFException异常是预期的,因此当它发生时似乎没有问题。任何其他异常都应该被处理。

...
} catch (EOFException e) {
   // ... this is fine
} catch(IOException e) {
    // handle exception which is not expected
    e.printStackTrace(); 
}

1
这就是在数据流示例中所做的。它也能正常工作。 - Jonathan Lam

2

或者,您可以首先写出元素数量(作为标题),使用以下方法:

out.writeInt(prices.length);

当你读取文件时,首先读取头部(元素计数):

int elementCount = in.readInt();

for (int i = 0; i < elementCount; i++) {
     // read elements
}

是的,我可以使用那个,但比起 @yogendra singh 的建议来说会更不方便。 - Jonathan Lam
@Jon,是的,这样做也不太安全(文件可能为空等),只是作为一种替代方案展示。 - Katona

2
你可以使用while(in.available() != 0)代替while(true)

1
hasNextLine() 不是 DataInputStream 的一部分。这个答案是错误的。 - user1231232141214124
抱歉!我没有看到那个。我现在已经改了。 - akaHuman
这个答案仍然是错误的。InputStream.available() 不是用于测试流结束的方法。请参阅Javadoc。 - user207421

2
你可能会遇到读取InputStream并使用代码片段while(in.available()>0)来检查流的结束,而不是检查EOFException(文件结尾)的代码。这种技术的问题是,Javadoc也反映了这一点,它只告诉你可以读取多少个块而不会阻塞下一个调用者。换句话说,即使还有更多的字节需要读取,它也可能返回0。因此,InputStream available()方法不应该用于检查流的结束。
你必须使用while (true)和...
catch(EOFException e) {
//This isn't problem
} catch (Other e) {
//This is problem
}

0

EOFException是IOException的子类 我更喜欢以下写法 ==>

try {
        .
        .
        .
    } catch (IOException e) {
        if (!(e instanceof EOFException)) {
            throw new RuntimeException(e);
        }
    }

1
为什么要这样做呢?你可以只写一个单独的 catch (EOFException e) 块。这样做毫无意义。 - user207421
哈哈...没错...现在我觉得这很有趣XD...而且那个catch块应该放在IOException之前...@user207421 - Mandar Autade

0
您捕获了 IOException ,它也捕获 EOFException ,因为它被继承。如果您查看tutorial中的示例,它们强调您应该捕获 EOFException - 这就是他们所做的。要解决您的问题,请在捕获 IOException 之前捕获 EOFException
try
{
    //...
}
catch(EOFException e) {
    //eof - no error in this case
}
catch(IOException e) {
    //something went wrong
    e.printStackTrace(); 
}

此外,我不喜欢使用异常来控制数据流 - 这不是异常的预期用途,因此(在我看来)这样做非常糟糕。


这正是他的异常的“预期用途”。你“原则”存在的问题在于,异常本身就是一种流程控制形式。 - user207421

-1
将你的代码放到 try catch 块中: 例如:

try{
  if(in.available()!=0){
    // ------
  }
}catch(EOFException eof){
  //
}catch(Exception e){
  //
}
}

available()测试没有任何有用的目的。 - user207421

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接