【问题标题】:Java 8 how to read file twice and fix java.io.IOException: Stream closedJava 8 如何读取文件两次并修复 java.io.IOException: Stream closed
【发布时间】:2021-02-17 10:27:23
【问题描述】:

我试图读取文本文件两次以计算平均值 (calcAverage),然后对平均值进行过滤以获得结果列表 (processFile)。第二步运行异常时,
java.io.UncheckedIOException: java.io.IOException: Stream closed 被抛出。

下面是我的失败代码的简化版本和驱动代码的单元测试。

Reader 类型的参数 (source) 从单元测试传递到 App,并且是文本文件的 FileReader。我不知道如何从Reader 对象访问File 处理程序(或文件名)以重新打开它 - 我已经尝试在 App 中实现它,这将解决问题。 runProcess (Reader source) 的方法签名不能更改 - 但其他方法签名可以。

我正在使用try-with-resources 块来打开 Reader 对象并通读它 - 然后它会自动关闭 - 这一切都很好。我只需要一种从Reader 重新打开文件的方法,以执行 pass-2 的过滤。

我从类似的问题中读到,BufferedReader 就像一个迭代器,你只能阅读一次。

我曾尝试在我的source 对象上使用mark() 和reset() 方法,但这会引发不支持这些方法的异常。

我可以将整个文件读入 List 对象,然后使用它来计算两个步骤,但我不知道我的文件会有多大,因此如果可能的话,我想尝试使用以下方法找到解决方案。

有谁知道我该如何实现这个?

    public class App {
    
        public static void runProcess(Reader source) {
            Collection<?> col = calcAverage(source);
            processFile(source).forEach(x -> System.out.println(x));
        }
    
        private static Collection processFile(Reader source)  {
            Collection<?> col = processFile(source, ((BufferedReader reader) -> reader.lines()
                    .skip(1)
                    .collect(Collectors.toList()))
            );
            return col;
        }
    
        private static Collection<?> calcAverage(Reader source) {
            Collection<?> col = processFile(source, ((BufferedReader reader) -> reader.lines()
                    .skip(1)
                    .collect(Collectors.toList())));
            return col;
        }
    
        private static Collection<?> processFile(Reader source, BufferedReaderProcessor p){
            try (BufferedReader reader = new BufferedReader(source)) {
                return p.process(reader);
            }catch (FileNotFoundException f){
                f.printStackTrace();
                return null;
            }catch (IOException e){
                e.printStackTrace();
                return null;
            }catch (Exception ex){
                ex.printStackTrace();
                return null;
            }
        }
    
        @FunctionalInterface
        public interface BufferedReaderProcessor {
            Collection<?> process(BufferedReader b) throws IOException;
        }
    }
  
    public class AppTest {

       @Test
       public void shouldReadFileTwice() throws FileNotFoundException {
           App.runProcess(openFile("src/main/java/functions/example4/resources/list-of-fruits"));
       }

       private static Reader openFile(String filename) throws FileNotFoundException {
           return new FileReader(new File(filename));
       }

    }   

【问题讨论】:

  • processFile() 将文件加载到集合中,因此看起来文件小到足以保存在内存中。最简单的方法可能是先执行此操作,然后处理加载的值,因此您只需读取文件一次。
  • calcAverage 关闭source,之后就不能使用了。您可以从 runProcesss 中删除 processFile,将其公开,然后将 shouldReadFileTwice 更改为具有第二行 App.processFile(openFile())。您还可以让 runProcess 将这些行读入一个集合,然后将该集合传递给 calcAverage 和 processFile,这样您只需将文件打开一次并将其读入内存然后再传递它。
  • 除此之外,您的异常处理程序所做的一切都是一样的,catch(Exception ex) 无论如何都会捕获所有其他异常。所以其他处理程序已经过时(虽然return null; 不是处理它的好方法)。
  • 想想你希望调用者如何处理异常情况。目前,他们不检查null的结果...
  • “抛出异常并让调用者处理它”是最简洁的选项。仅当空集合不能在非异常情况下出现或处理异常情况和空集合对应用程序有效时,才可能返回空集合。

标签: java-8 bufferedreader filereader ioexception


【解决方案1】:

我认为您不应该对资源使用 try。它在其 BufferedReader 上调用 close,这会导致所有封装的阅读器都关闭。 IE。它关闭了BufferedReader、Reader 和FileReader。

当您在App::runProcess 中调用calcAverage 时,它会关闭所有阅读器。然后你在下一行调用processFile 时尝试读取已关闭的Reader。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-08-11
    • 1970-01-01
    • 2023-03-25
    • 2014-05-18
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多