OutOfMemoryError读取具有大行的174 Mb文本文件

rev*_*evy 5 java file bufferedreader

我有12000行的csv文件。每行都有几个用双引号引起来并用逗号分隔的字段。此字段之一是xml文档,因此该行可能很长。文件大小为174 Mb。

这是文件的示例:

"100000","field1","field30","<root><data>Hello I have a
line break</data></root>","field31"
"100001","field1","field30","<root><data>Hello I have multiple
line 
break</data></root>","field31"
Run Code Online (Sandbox Code Playgroud)

此文件的问题在xml字段内,该字段可能具有一个或多个换行符,因此可能会中断解析。此处的目标是读取整个文件并应用正则表达式,它将用空字符串替换双引号内的所有换行符。

以下代码给了我OutOfMemoryError:

    String path = "path/to/file.csv";

    try {
        byte[] content = Files.readAllBytes(Paths.get(path));
    }
    catch (Exception e) {
        e.printStackTrace();
        System.exit(1);
    }
Run Code Online (Sandbox Code Playgroud)

我还尝试使用BufferedReader和StringBuilder读取文件,在第5000行附近出现OutOfMemoryError:

String path = "path/to/file.csv";

    try {
        StringBuilder sb = new StringBuilder();
        BufferedReader br = new BufferedReader(new FileReader(path));
        String line;
        int count = 0;
        while ((line = br.readLine()) != null) {
            sb.append(line);
            System.out.println("Read " + count++);
        }
    }
    catch (Exception e) {
        e.printStackTrace();
        System.exit(1);
    }
Run Code Online (Sandbox Code Playgroud)

我尝试使用不同的Java堆值(如-Xmx1024m,-Xmx4096m,-Xmx8092m)运行以上两个程序。在所有情况下,我都遇到了OutOfMemoryError。考虑到文件大小为174Mb,为什么会发生这种情况?

gab*_*sch 3

您需要使用双缓冲区来解析您的特殊数据结构,并逐行处理它们。阅读整个文档并不是最好的主意。

创建一个自己的BufferedReader文件来读取BufferedReaderCSV 文件内部的行。读取一行后,尝试确定是否需要读取更多行来完成 CSV 中的一行(例如,如果您知道 XML 以 开头<root>和结尾</root>,请检查这些字符串是否存在,然后读取并追加,直到到达结束标记 - 这将是 CSV 行的最后一行)。

第二层将是您的 CSV 处理,基于您从第一步获得的 CSV 行。解析它,保存它,处理它,然后抛出它。那么它就不会消耗更多的内存空间,Java垃圾收集器会释放它。

这是处理大文件的唯一方法。它也被称为“流模型”,因为你只传递小块数据,所以实际的内存消耗很低。