修改大文件内容
Modify content of large file
我已经从我的数据库中的 json 文件中提取了我的表,现在我想读取这个文件并删除它们上面的所有双引号,看起来很简单并尝试了数百种解决方案,有些将我带到了内存不足的问题。我正在处理超过 1Gb size.The 代码的文件,你会发现下面的代码有一个奇怪的行为,我不明白为什么它 return 空文件
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);
}
}
当我尝试使用FileWriter(path,true)
写入文件末尾时,程序不会停止增加文件内存,直到硬盘已满,感谢您的帮助
ps :我也尝试使用 subString 并附加新内容,然后我写了 subString 但也不起作用
TL;博士;
不要同时读写同一个文件。
问题
您的代码开始读取,然后立即截断它正在读取的文件。
reader = new BufferedReader(new FileReader(firstFile));
writerFile = new FileWriter("C:/sqlite/db/tables/" + fileName);
writer = new BufferedWriter(writerFile);
第一行打开文件的读句柄。
第二行打开同一个文件的写句柄。
如果你看FileWriter构造函数的文档不是很清楚,但是当你不使用允许你指定append
参数的构造函数时,默认值为false
,意思是,如果文件已经存在,您将立即截断该文件。
此时(第 2 行)您刚刚删除了您要阅读的文件。所以你最终得到一个空文件。
使用 append=true 怎么样
嗯,那么文件在创建的时候没有被擦除,就“好”了。所以你的程序开始读取第一行,并输出(到同一个文件)过滤后的版本。
因此每次读取一行时,都会附加另一行。
难怪您的程序永远不会到达文件末尾:每次前进一行时,都会创建另一行进行处理。一般来说,您永远不会到达文件末尾(当然,如果文件是单行开头,您可能会到达,但这是一个极端情况)。
解决方案
写入一个临时文件,如果(且仅当)您成功了,然后在确实需要时交换文件。
此解决方案的优势:如果由于某种原因您的进程崩溃,您将保持原始文件不变,您可以稍后重试,这通常是一件好事。您的过程是“可重复的”。
缺点:在某些时候您需要 space 的两倍。 (虽然你可以压缩临时文件并减少这个因素但仍然)。
关于内存不足问题
处理任意大的文件时,您选择的路径(使用缓冲的 reader 和编写器)是正确的,因为您一次只使用一个 line-worth 内存。
因此它通常可以避免内存使用问题(当然,除非你有一个没有换行符的文件,在这种情况下它根本没有区别)。
其他解决方案,包括一次读取整个文件,然后在内存中执行 search/replace,然后将内容写回,并不能很好地缩放,所以最好避免这种计算。
不相关但重要
检查 try with resources 语法以正确关闭您的资源(reader / 作者)。在这里你忘了关闭 reader,而且你也没有适当地关闭 writer(即:在 finally 子句中)。
另一件事:我敢肯定,凡人编写的 java 程序都无法击败像 sed
或 awk
这样的大多数 unix 平台(以及一些更多的)。也许您想检查自己在 java 中滚动是否值得 shell one-liner.
@GPI 已经提供了一个很好的答案,说明为什么同时读取和写入会导致您遇到的问题。同样值得注意的是,如果没有分配足够的堆,一次将 1gb 的数据读入堆肯定会导致 OutOfMemoryError
,这很可能。要解决这个问题,您可以使用 InputStream
并一次读取文件块,然后写入另一个文件直到该过程完成,最后用修改后的文件替换现有文件并删除。使用这种方法,您甚至可以使用 ForkJoinTask
来帮助解决这个问题,因为这是一项庞大的工作。
旁注;
可能有比 create new file, write to new file, replace existing, delete new file
.
更好的解决方案
我已经从我的数据库中的 json 文件中提取了我的表,现在我想读取这个文件并删除它们上面的所有双引号,看起来很简单并尝试了数百种解决方案,有些将我带到了内存不足的问题。我正在处理超过 1Gb size.The 代码的文件,你会发现下面的代码有一个奇怪的行为,我不明白为什么它 return 空文件
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);
}
}
当我尝试使用FileWriter(path,true)
写入文件末尾时,程序不会停止增加文件内存,直到硬盘已满,感谢您的帮助
ps :我也尝试使用 subString 并附加新内容,然后我写了 subString 但也不起作用
TL;博士;
不要同时读写同一个文件。
问题
您的代码开始读取,然后立即截断它正在读取的文件。
reader = new BufferedReader(new FileReader(firstFile));
writerFile = new FileWriter("C:/sqlite/db/tables/" + fileName);
writer = new BufferedWriter(writerFile);
第一行打开文件的读句柄。
第二行打开同一个文件的写句柄。
如果你看FileWriter构造函数的文档不是很清楚,但是当你不使用允许你指定append
参数的构造函数时,默认值为false
,意思是,如果文件已经存在,您将立即截断该文件。
此时(第 2 行)您刚刚删除了您要阅读的文件。所以你最终得到一个空文件。
使用 append=true 怎么样
嗯,那么文件在创建的时候没有被擦除,就“好”了。所以你的程序开始读取第一行,并输出(到同一个文件)过滤后的版本。
因此每次读取一行时,都会附加另一行。
难怪您的程序永远不会到达文件末尾:每次前进一行时,都会创建另一行进行处理。一般来说,您永远不会到达文件末尾(当然,如果文件是单行开头,您可能会到达,但这是一个极端情况)。
解决方案
写入一个临时文件,如果(且仅当)您成功了,然后在确实需要时交换文件。
此解决方案的优势:如果由于某种原因您的进程崩溃,您将保持原始文件不变,您可以稍后重试,这通常是一件好事。您的过程是“可重复的”。
缺点:在某些时候您需要 space 的两倍。 (虽然你可以压缩临时文件并减少这个因素但仍然)。
关于内存不足问题
处理任意大的文件时,您选择的路径(使用缓冲的 reader 和编写器)是正确的,因为您一次只使用一个 line-worth 内存。
因此它通常可以避免内存使用问题(当然,除非你有一个没有换行符的文件,在这种情况下它根本没有区别)。
其他解决方案,包括一次读取整个文件,然后在内存中执行 search/replace,然后将内容写回,并不能很好地缩放,所以最好避免这种计算。
不相关但重要
检查 try with resources 语法以正确关闭您的资源(reader / 作者)。在这里你忘了关闭 reader,而且你也没有适当地关闭 writer(即:在 finally 子句中)。
另一件事:我敢肯定,凡人编写的 java 程序都无法击败像 sed
或 awk
这样的大多数 unix 平台(以及一些更多的)。也许您想检查自己在 java 中滚动是否值得 shell one-liner.
@GPI 已经提供了一个很好的答案,说明为什么同时读取和写入会导致您遇到的问题。同样值得注意的是,如果没有分配足够的堆,一次将 1gb 的数据读入堆肯定会导致 OutOfMemoryError
,这很可能。要解决这个问题,您可以使用 InputStream
并一次读取文件块,然后写入另一个文件直到该过程完成,最后用修改后的文件替换现有文件并删除。使用这种方法,您甚至可以使用 ForkJoinTask
来帮助解决这个问题,因为这是一项庞大的工作。
旁注;
可能有比 create new file, write to new file, replace existing, delete new file
.