我有一个包含12000行的csv文件。 每行都有几个用双引号引起来并用逗号分隔的字段。此字段之一是xml文档,因此该行可能很长 strong>。文件大小为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"
此文件的问题在xml字段内,该字段可能具有一个或多个换行符,因此可能会中断解析。这里的目标是读取整个文件并应用一个正则表达式,它将用空字符串替换双引号内的所有换行符。
以下代码给了我OutOfMemoryError:
String path = "path/to/file.csv";
try {
byte[] content = Files.readAllBytes(Paths.get(path));
}
catch (Exception e) {
e.printStackTrace();
System.exit(1);
}
我还尝试使用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);
}
我尝试使用不同的Java堆值(例如-Xmx1024m,-Xmx4096m,-Xmx8092m)运行以上两个程序。在所有情况下,我都遇到了OutOfMemoryError。 考虑到文件大小为174Mb,为什么会发生这种情况?
答案 0 :(得分:3)
您需要使用双缓冲区来解析您的特殊数据结构,并逐行处理它们。阅读整个文档不是最好的主意。
创建一个自己的BufferedReader
,该行读取带有CSV文件内部BufferedReader
的行。
读取一行后,尝试确定是否需要阅读更多行以完成CSV中的一行(例如,如果您知道XML以<root>
开头和以</root>
结尾,请检查是否存在这些行字符串,然后读取并追加直到您到达结束标记-这将是CSV行的最后一行)。
第二层将是CSV处理,基于您从第一步获得的CSV行。对其进行解析,保存,处理,然后将其抛出。这样它就不会消耗更多的内存空间,Java垃圾收集器将释放它。
这是处理大文件的唯一方法。它也称为“流模型”,因为您只传递一小部分数据,因此实际内存消耗较低。
答案 1 :(得分:2)
用过滤器将InputStream包裹起来:
class QuotedNewLineFilterInputStream extends FilterInputStream {
private boolean insideQuotes;
public QuotedNewLineFilterInputStream(InputStream in) {
super(in);
}
@Override
public int read() throws IOException {
int c = super.read();
if (c == '\"') {
insideQuotes = !insideQuotes;
}
if (insideQuotes && (c == '\n' || c == '\r')) {
c = read();
}
return c;
}
}
这将删除双引号内的LF和CR。由于全部都是ASCII,并且XML可能在UTF-8中使用,因此可以在字节级别(InputStream)上工作。
用\t
替代可能会更好地保留布局(c =
\ t'i.o. c = read()
)。
不是很聪明,但是简单的解决方案。
答案 2 :(得分:0)
如果使用Files.readAllBytes(Paths.get(path));
读取174 MB的文件导致OutOfMemoryError
,则无法通过-Xmx8g
增加内存限制。有了8 GB的堆内存,为byte[]
再次检查您如何通过-Xmx
标志。您可以通过使用JConsole,JVisualVM或其他工具连接到正在运行的JVM证明来验证JVM运行时选项。看一下Using JConsole,它显示了如何检查JVM运行时选项,例如内存标签。