【问题标题】:Why is BufferedReader readLine reading past EOF为什么 BufferedReader readLine 读取超过 EOF
【发布时间】:2023-03-21 05:11:01
【问题描述】:

我有一个非常大的文件 (~6GB),其中包含由 \r\n 分隔的固定宽度文本,因此我使用缓冲阅读器逐行读取。这个过程可以被中断或停止,如果是,它使用检查点“lastProcessedLineNbr”快进到正确的地方继续阅读。这就是阅读器的初始化方式。

private void initializeBufferedReader(Integer lastProcessedLineNbr) throws IOException {
    reader = new BufferedReader(new InputStreamReader(getInputStream(), "UTF-8"));
    if(lastProcessedLineNbr==null){lastProcessedLineNbr=0;}

    for(int i=0; i<lastProcessedLineNbr;i++){
        reader.readLine();
    }
    currentLineNumber = lastProcessedLineNbr;
}

这似乎工作正常,我用这种方法读取和处理数据:

public Object readItem() throws Exception {
    if((currentLine = reader.readLine())==null){
        return null;
    }
    currentLineNumber++;
    return parse(currentLine);
}

再一次,一切正常,直到我到达文档的最后一行。后一种方法中的 readLine() 会抛出错误:

17:06:49,980 ERROR [org.jberet] (Batch Thread - 1) JBERET000007: Failed to run job ProdFileRead, parse, org.jberet.job.model.Chunk@3965dcc8: java.lang.OutOfMemoryError: Requested array size exceeds VM limit
    at java.util.Arrays.copyOf(Arrays.java:3332)
    at java.lang.AbstractStringBuilder.expandCapacity(AbstractStringBuilder.java:137)
    at java.lang.AbstractStringBuilder.ensureCapacityInternal(AbstractStringBuilder.java:121)
    at java.lang.AbstractStringBuilder.append(AbstractStringBuilder.java:569)
    at java.lang.StringBuffer.append(StringBuffer.java:369)
    at java.io.BufferedReader.readLine(BufferedReader.java:370)
    at java.io.BufferedReader.readLine(BufferedReader.java:389)
    at com.rational.batch.reader.TextLineReader.readItem(TextLineReader.java:55)

奇怪的是,它似乎正在读取文件末尾并分配太多空间以致内存不足。我尝试使用 Cygwin 和“tail file.txt”查看文件的内容,在控制台中它给了我预期的 10 行。但是当我执行“tail file.txt > output.txt”时,output.txt 最终变成了 1.8GB,比我预期的 10 行大得多。所以看起来 Cygwin 也在做同样的事情。据我所知,没有特殊的 EOF 字符。它只是数据的最后一个字节,它突然结束。

任何人都知道我怎样才能让它工作?我想我可以求助于计算读取的字节数,直到我得到文件的完整大小,但我希望有更好的方法。

【问题讨论】:

  • 当然,我自己也想到将 EOF 添加到文件中,但我也不确定放置 EOF 字符的 java 方式是什么,因为这是特定于操作系统的。我希望它可以在 Windows 和 Linux 中运行。
  • 在 Windows 或 Linux 上都没有“EOF 字符”。 Ctrl-D 或 Ctrl-Z 由终端 I/O 子系统解释并向读取进程发送 EOF 信号。将其中一个放在文件中不会产生预期的效果。真正的问题是“最后”记录中文件的内容是什么。从异常看来,最后一个“行”非常大,大到足以溢出可用内存。您确定所有行都是\r\n 分隔的吗?文件最后一行之后是否分配了空白空间,正在返回?

标签: java file bufferedreader eof


【解决方案1】:

但是当我这样做时,tail file.txt &gt; output.txt output.txt 最终变成了 1.8GB,比我预期的 10 行大得多

这向我表明,该文件填充了 1.8GB 的​​二进制零,Cygwin 的 tail 命令在写入终端时忽略了它,但 Java 没有忽略它。这也可以解释您的OutOfMemoryError,因为BufferedReader 继续读取数据以寻找下一个\r\n,在内存溢出之前从未找到它。

【讨论】:

  • 可能是终端忽略了所有的空值。
  • @EJP 你是对的。我刚刚检查了最后 10000 个字节,它们都是空的。我只是倾向于责怪我的编程逻辑而不是输入。对我来说是一个很好的教训。哈哈。谢谢你们。
猜你喜欢
  • 1970-01-01
  • 2021-02-14
  • 1970-01-01
  • 2014-09-26
  • 2017-05-10
  • 1970-01-01
  • 2012-09-30
  • 1970-01-01
相关资源
最近更新 更多