修改大文件内容

问题描述

我已经从数据库中的json文件提取了我的表,现在我想读取该文件删除所有双引号,这似乎很简单,并尝试了数百种解决方案,其中一些使我陷入内存不足的问题。我正在处理大小超过1Gb的文件。您将在下面找到的代码具有奇怪的行为,而且我不明白为什么它返回空文件

export PYTHONPATH=...

当我尝试使用 public void replaceDoubleQuotes(String fileName){ log.debug(" start formatting " + fileName + " ..."); File firstFile = new File ("C:/sqlite/db/tables/" + fileName); String oldContent = ""; String newContent = ""; BufferedReader reader = null; BufferedWriter writer = null; FileWriter writerFile = null; String stringQuotes = "\\\\\\\\\""; try { reader = new BufferedReader(new FileReader(firstFile)); writerFile = new FileWriter("C:/sqlite/db/tables/" + fileName); writer = new BufferedWriter(writerFile); while (( oldContent = reader.readLine()) != null ){ newContent = oldContent.replaceAll(stringQuotes,""); writer.write(newContent); } writer.flush(); writer.close(); } catch (Exception e) { log.error(e); } } 写入文件末尾时,程序不会停止增加文件内存,直到硬盘将满,谢谢您的帮助

ps:我也尝试使用subString并追加新内容,并且在写subString之后也无法使用

解决方法

TL; DR;

请勿同时读写同一文件。

问题

您的代码开始读取,然后立即截断正在读取的文件。

 reader = new BufferedReader(new FileReader(firstFile));
 writerFile = new FileWriter("C:/sqlite/db/tables/" + fileName);
 writer = new BufferedWriter(writerFile);
    

第一行打开文件的读取句柄。 第二行打开对同一文件的写句柄。 如果您查看FileWriter构造函数的文档还不太清楚,但是当您不使用允许您指定append参数的构造函数时,默认情况下该值为false,这意味着,您会立即截断该文件(如果已存在)。

在这一点上(第2行),您刚刚删除了将要读取的文件。因此,您最终得到一个空文件。

如何使用append = true

好吧,那么在创建文件时不会将其删除,这是“好”的。因此,您的程序开始读取第一行,然后将过滤后的版本输出(到同一文件中)。

因此,每次读取一行时,都会添加另一行。

难怪您的程序永远不会到达文件末尾:每次前进一行时,它都会创建另一行要处理。一般来说,您永远都不会到达文件末尾(当然,如果文件以单行开头,则可能会出现这种情况,但这只是一个极端情况)。

解决方案

写入临时文件,然后成功(如果只有IF)写入文件,然后在确实需要时交换文件。

该解决方案的一个优势:如果由于某种原因导致进程崩溃,则原始文件将保持不变,您可以稍后重试,这通常是一件好事。您的过程是“可重复的”。

一个缺点:在某个时候您将需要两倍的空间。 (尽管您可以压缩临时文件并减小此因子,但仍然可以)。

关于内存不足问题

在处理任意大文件时,您选择的路径(使用缓冲的读取器和写入器)是正确的路径,因为一次只能使用一行的内存。

因此,它通常可以避免内存使用问题(当然,除非您的文件没有换行符,在这种情况下,它根本没有区别)。

其他解决方案包括一次读取整个文件,然后在内存中执行搜索/替换,然后将内容写回,因此无法很好地缩放比例,因此最好避免这种计算。

无关但重要

签出try with resources语法以正确关闭资源(读取器/写入器)。在这里,您忘记了关闭阅读器,而无论如何也没有适当地关闭书写器(即,在finally子句中)。

另一件事:我敢肯定,凡人编写的Java程序都不会击败大多数UNIX平台(以及更多)上可用的sedawk之类的工具。也许您想检查一下在Java中滚动自己是否值得一站式Shell。

,

@GPI已经为为什么同时进行读写导致您遇到的问题提供了一个很好的答案。还值得注意的是,如果未分配足够的堆,一次将1gb的数据读取到堆中肯定会导致OutOfMemoryError。要解决此问题,您可以使用InputStream一次读取文件的大块,然后写入另一个文件直到该过程完成,并最终用修改后的文件替换并删除现有文件。通过这种方法,您甚至可以使用ForkJoinTask来解决此问题,因为它的工作量很大。

旁注; create new file,write to new file,replace existing,delete new file更好的解决方案。