【问题标题】:Files.lines to skip broken lines in Java8Files.lines 在 Java8 中跳过断行
【发布时间】:2014-09-26 17:03:04
【问题描述】:

我正在使用 Files.lines(...) 读取一个非常大 (500mb) 的文件。 它读取文件的一部分,但在某些时候它会因 java.io.UncheckedIOException: java.nio.charset.MalformedInputException: Input length = 1

而中断

我认为该文件有不同字符集的行。有没有办法跳过这些断线?我知道返回的流由 Reader 支持,并且我知道如何跳过 Reader,但不知道如何从流中获取 Reader 以按照我的喜好进行设置。

    List<String> lines = new ArrayList<>();
    try (Stream<String> stream = Files.lines(Paths.get(getClass().getClassLoader().getResource("bigtest.txt").toURI()), Charset.forName("UTF-8"))) {
        stream
            .filter(s -> s.substring(0, 2).equalsIgnoreCase("aa"))
            .forEach(lines::add);
    } catch (final IOException e) {
        // catch
    }

【问题讨论】:

    标签: java file java-8 java-stream


    【解决方案1】:

    当预配置的解码器已经异常停止解码时,您无法过滤解码后包含无效字符的行。您必须手动配置 CharsetDecoder 以告诉它忽略无效输入或用特殊字符替换该输入。

    CharsetDecoder dec=StandardCharsets.UTF_8.newDecoder()
                      .onMalformedInput(CodingErrorAction.IGNORE);
    Path path=Paths.get(getClass().getClassLoader().getResource("bigtest.txt").toURI());
    List<String> lines;
    try(Reader r=Channels.newReader(FileChannel.open(path), dec, -1);
        BufferedReader br=new BufferedReader(r)) {
            lines=br.lines()
                    .filter(s -> s.regionMatches(true, 0, "aa", 0, 2))
                    .collect(Collectors.toList());
    }
    

    这只是忽略字符集解码错误,跳过字符。要跳过包含错误的整行,您可以让解码器为错误插入替换字符(默认为 '\ufffd')并过滤掉包含该字符的行:

    CharsetDecoder dec=StandardCharsets.UTF_8.newDecoder()
                      .onMalformedInput(CodingErrorAction.REPLACE);
    Path path=Paths.get(getClass().getClassLoader().getResource("bigtest.txt").toURI());
    List<String> lines;
    try(Reader r=Channels.newReader(FileChannel.open(path), dec, -1);
        BufferedReader br=new BufferedReader(r)) {
            lines=br.lines()
                    .filter(s->!s.contains(dec.replacement()))
                    .filter(s -> s.regionMatches(true, 0, "aa", 0, 2))
                    .collect(Collectors.toList());
    }
    

    【讨论】:

    • 感谢您的回答霍尔格。我还想知道是否可以使用流来避免样板代码,但它看起来是不可能的(流由阅读器支持,我希望有可能以某种方式获得阅读器并添加解码器)
    • 该 API 不支持。但即使是这样,它也不会比这里的代码更紧凑。请注意,“样板”只是另外一行。结果看起来更冗长,只是因为我不喜欢水平滚动,所以我做了更慷慨的格式。好吧,在为更广泛的受众发布代码示例时,我使用的空白比我在真实代码中所做的要多。当然,您可以内联decpath 并将import static 用于StandardCharsets.UTF_8 以及CodingErrorAction.*Channels.newReaderFileChannel.open 等。
    • 是的,我同意。再次感谢霍尔格。
    【解决方案2】:

    在这种情况下,使用 Streams API 时,解决方案将变得复杂且更容易出错。我建议只使用普通的 for 循环从 BufferedReader 读取,然后捕获 MalformedInputException。这也可以区分其他 IO 异常的捕获:

    List<String> lines = new ArrayList<>();
    
    try (BufferedReader r = new BufferedReader(path,StandardCharsets.UTF_8)){
         try{
              String line = null;
              while((line=r.readLine())!=null){
                   if(line.substring(0, 2).equalsIgnoreCase("aa")){
                        lines.add(line);
                    }
         }catch(MalformedInputException mie){
               // ignore or do something
         }
    }
    

    【讨论】:

    • 感谢您的评论,我有几个注意事项: * 通常使用函数式样式使代码更清晰、更简洁。目前有几行代码,但如果它增长,我认为函数式是要走的路 * 你正在通过异常进行编程,这是我不太喜欢的一种做法 * 你会失去懒惰和它的所有好处(简单的并行化,无状态代码,...) 说了这么多,你的解决方案绝对是另一个可行的解决方案。
    • @Fra 好点。霍尔格的回答更为详尽。但是,我的“懒惰”并没有损失,因为它也很懒惰。但是,Stream 的异常处理起来很复杂。在您必须调试之前看起来不错:) 此外,在 Holgers 解决方案中,您仍然需要捕获和处理 IOException。除非你完全忽略这一点并放弃它。最大的问题是使用 Stream-filter 范例,该代码非常慢。对于 500 MB 的大文件,我会避免使用过滤器。
    猜你喜欢
    • 2019-07-17
    • 2023-03-26
    • 2021-03-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多