合并大文件而不将整个文件加载到内存中?
Posted
技术标签:
【中文标题】合并大文件而不将整个文件加载到内存中?【英文标题】:Merge huge files without loading whole file into memory? 【发布时间】:2014-10-22 04:40:33 【问题描述】:我想将包含字符串的大文件合并到一个文件中,并尝试使用 nio2。我不想将整个文件加载到内存中,所以我用 BufferedReader 进行了尝试:
public void mergeFiles(filesToBeMerged) throws IOException
Path mergedFile = Paths.get("mergedFile");
Files.createFile(mergedFile);
List<Path> _filesToBeMerged = filesToBeMerged;
try (BufferedWriter writer = Files.newBufferedWriter(mergedFile,StandardOpenOption.APPEND))
for (Path file : _filesToBeMerged)
// this does not work as write()-method does not accept a BufferedReader
writer.append(Files.newBufferedReader(file));
catch (IOException e)
System.err.println(e);
我用这个试过了,这行得通,但是,字符串的格式(例如,新行等不会复制到合并文件中):
...
try (BufferedWriter writer = Files.newBufferedWriter(mergedFile,StandardOpenOption.APPEND))
for (Path file : _filesToBeMerged)
// writer.write(Files.newBufferedReader(file));
String line = null;
BufferedReader reader = Files.newBufferedReader(file);
while ((line = reader.readLine()) != null)
writer.append(line);
writer.append(System.lineSeparator());
reader.close();
catch (IOException e)
System.err.println(e);
...
如何在不将整个文件加载到内存的情况下将大文件与 NIO2 合并?
【问题讨论】:
【参考方案1】:如果您想有效地合并两个或多个文件,您应该问自己,到底为什么要使用基于Reader
和Writer
的char
来执行该任务。
通过使用这些类,您可以将文件的字节转换为字符,从系统的默认编码转换为 unicode,然后从 unicode 转换为系统的默认编码。这意味着程序必须对整个文件进行两次数据转换。
顺便说一下,BufferedReader
和 BufferedWriter
绝不是 NIO2
工件。这些类从 Java 的第一个版本开始就存在。
当您通过真正的 NIO 函数使用逐字节复制时,可以在不被 Java 应用程序接触的情况下传输文件,在最好的情况下,传输将直接在文件系统的缓冲区中执行:
import static java.nio.file.StandardOpenOption.*;
import java.io.IOException;
import java.nio.channels.FileChannel;
import java.nio.file.Path;
import java.nio.file.Paths;
public class MergeFiles
public static void main(String[] arg) throws IOException
if(arg.length<2)
System.err.println("Syntax: infiles... outfile");
System.exit(1);
Path outFile=Paths.get(arg[arg.length-1]);
System.out.println("TO "+outFile);
try(FileChannel out=FileChannel.open(outFile, CREATE, WRITE))
for(int ix=0, n=arg.length-1; ix<n; ix++)
Path inFile=Paths.get(arg[ix]);
System.out.println(inFile+"...");
try(FileChannel in=FileChannel.open(inFile, READ))
for(long p=0, l=in.size(); p<l; )
p+=in.transferTo(p, l-p, out);
System.out.println("DONE.");
【讨论】:
哇,这个解决方案真的很棒——而且源代码很短。谢谢!您知道基于 nio2 的解决方案,用于将大文件拆分为一组小文件吗?实际上,我正在使用类似 todayguesswhat.blogspot.de/2014/05/…. @nimo23: 好吧,我想,当你尝试理解我的答案的代码时,尤其是FileChannel.transferTo
所做的事情,你会意识到拆分解决方案的样子(阅读:非常相似)。如果您在实施时遇到困难,可以提出一个新问题。
好的,我自己试试,这里会提供解决方案!
好的,我已经发布了解决方案:***.com/questions/25553673/…。我找不到使用 nio2 的解决方案,因为使用 nio2 拆分文件的大小只能通过文件大小来减小。但是,我想按行号拆分文本文件。您是否找到了使用 nio2 的 splitTextFiles()-Method 的(更好的)解决方案?【参考方案2】:
readLine()
不会产生行尾(“\n”或“\r\n”)。那是错误。
while ((line = reader.readLine()) != null)
writer.write(line);
writer.write("\r\n"); // Windows
您也可以忽略对(可能不同)行尾的过滤,并使用
try (OutputStream out = new FileOutputStream(file);
for (Path source : filesToBeMerged)
Files.copy(path, out);
out.write("\r\n".getBytes(StandardCharsets.US_ASCII));
这会显式写入换行符,以防最后一行不以换行符结尾。
在文件开头将文本标记为 UTF-8/UTF-16LE/UTF-16BE 的可选的、丑陋的 Unicode BOM 字符可能仍然存在问题。
【讨论】:
【参考方案3】:有
Files.newBufferedReader(file).readLine()
你每次都创建一个新的缓冲区,它总是在第一行重置。
替换为
BufferedReader reader = Files.newBufferedReader(file);
while ((line = reader.readLine()) != null)
writer.write(line);
和.close()
完成后的读者。
【讨论】:
谢谢,我在源代码中做了修改。你知道,我怎样才能将合并文件的格式保留为“mergedFile”-File?例如,合并的文件有回车或空行。使用上述方法时,所有这些都不会复制到“mergedFile”中。 不确定您的意思,但您可以使用 writer.write(System.lineSeparator()); 手动追加换行符; 我想知道哪个性能更好。以上解决方案或programcreek.com/2012/09/merge-files-in-java中的解决方案。你知道哪一个性能更好? @nimo23 为它写一个测试。你有一个大文件,所以执行复制几次并检查一种方法花费了多少时间,另一种方法花费了多少时间。以上是关于合并大文件而不将整个文件加载到内存中?的主要内容,如果未能解决你的问题,请参考以下文章
Actionscript 3,只能读取文件的一部分而不将整个文件加载到内存中