修改大文件的内容

k5ifujac  于 2021-07-12  发布在  Java
关注(0)|答案(2)|浏览(289)

我已经从json文件中的数据库中提取了我的表,现在我想读取这些文件并删除它们上的所有双引号,看起来很简单,尝试了上百种解决方案,有些解决方案将我带到内存不足的问题。我处理的文件大小超过1gb。下面的代码有一个奇怪的行为,我不明白为什么它返回空文件

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:我也尝试过使用子字符串并附加新内容,之后虽然我写了子字符串,但也不起作用

acruukt9

acruukt91#

热释光;博士;

不要同时读写同一个文件。

问题

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

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

第一行打开文件的读取句柄。第二行打开同一文件的写入句柄。如果您查看filewriter构造函数的文档,但不使用允许您指定 append 参数,则值为 false 默认情况下,也就是说,如果文件已经存在,您会立即截断它。
此时(第2行)您刚刚删除了要读取的文件。所以你最终得到了一个空文件。

使用append=true怎么样

好吧,那么文件在创建时不会被删除,这是“好的”。因此,您的程序开始读取第一行,并输出(到同一个文件)过滤后的版本。
因此,每次读取一行时,都会追加另一行。
难怪您的程序永远不会到达文件的末尾:每次它前进一行,它就会创建另一行来处理。一般来说,你永远不会到达文件的结尾(当然,如果文件是一行开始,你可以,但这是一个角落的情况)。

解决方案

写一个临时文件,如果(并且只有)你成功了,然后交换文件,如果你真的需要。
此解决方案的一个优点是:如果由于任何原因进程崩溃,您将保持原始文件不变,并且可以稍后重试,这通常是一件好事。你的过程是“可重复的”。
缺点:在某个时候你需要两倍的空间(虽然您可以压缩临时文件并减少这个因素,但仍然可以。

关于内存不足问题

在处理任意大的文件时,您选择的路径(使用缓冲读写器)是正确的,因为您一次只使用一行内存。
因此,它通常可以避免内存使用问题(当然,除非您有一个没有换行符的文件,在这种情况下没有任何区别)。
其他解决方案,包括一次读取整个文件,然后在内存中执行搜索/替换,然后将内容写回,都不能很好地伸缩,因此最好避免这种计算。

不相关但重要

检查try with resources语法以正确关闭资源(reader/writer)。在这里,您忘记关闭reader,并且您没有适当地关闭writer(即:在finally子句中)。
另一件事:我敢肯定,任何一个由凡人编写的java程序都无法打败像这样的工具 sed 或者 awk 在大多数unix平台上都可以使用。也许你应该检查一下用java开发自己的程序是否值得一个shell一行程序。

whhtz7ly

whhtz7ly2#

@gpi已经提供了一个很好的答案,解释了为什么同时读写会导致你所经历的问题。另外值得注意的是,一次将1gb的数据读入堆中肯定会导致 OutOfMemoryError 如果没有分配足够的堆,这是可能的。要解决这个问题,你可以使用 InputStream 并一次读取文件的块,然后写入另一个文件,直到过程完成,最后用修改后的文件替换现有文件并删除。使用这种方法,你甚至可以使用 ForkJoinTask 因为这是一个很大的工作。
旁注;也许有比这更好的解决办法 create new file, write to new file, replace existing, delete new file .

相关问题