按最大行分割非常大的文本文件

nim*_*o23 6 java java-8 nio2

我想将包含字符串的大文件拆分成一组新的(较小的)文件并尝试使用nio2.

我不想将整个文件加载到内存中,所以我尝试使用BufferedReader.

较小的文本文件应受文本行数的限制.

该解决方案有效,但我想询问是否有人通过使用java 8(也许lamdas with stream() - api?)和nio2知道一个具有更好性能的解决方案:

public void splitTextFiles(Path bigFile, int maxRows) throws IOException{

        int i = 1;
        try(BufferedReader reader = Files.newBufferedReader(bigFile)){
            String line = null;
            int lineNum = 1;

            Path splitFile = Paths.get(i + "split.txt");
            BufferedWriter writer = Files.newBufferedWriter(splitFile, StandardOpenOption.CREATE);

            while ((line = reader.readLine()) != null) {

                if(lineNum > maxRows){
                    writer.close();
                    lineNum = 1;
                    i++;
                    splitFile = Paths.get(i + "split.txt");
                    writer = Files.newBufferedWriter(splitFile, StandardOpenOption.CREATE);
                }

                writer.append(line);
                writer.newLine();
                lineNum++;
            }

            writer.close();
        }
}
Run Code Online (Sandbox Code Playgroud)

Hol*_*ger 4

请注意直接使用/及其子类与/ 的工厂方法之间的区别。在前一种情况下,当未给出显式字符集时,将使用 system\xe2\x80\x99s 默认编码,而后者始终默认为. 因此,我强烈建议始终指定所需的字符集,即使它是 \xe2\x80\x99s 或或来记录您的意图,并避免在创建或 的各种方式之间切换时出现意外。 InputStreamReaderOutputStreamWriterReaderWriter FilesUTF-8Charset.defaultCharset()StandardCharsets.UTF_8ReaderWriter

\n\n
\n\n

如果您想在行边界处分割,则无法查看文件\xe2\x80\x99s 的内容。所以你可以\xe2\x80\x99t像合并时那样优化它。

\n\n

如果您愿意牺牲可移植性,您可以尝试一些优化。如果您知道字符集编码将明确映射\'\\n\'(byte)\'\\n\'大多数单字节编码的情况,那么UTF-8您可以扫描字节级别的换行符以获取分割的文件位置并避免从应用程序到 I/O 系统的任何数据传输。

\n\n
public void splitTextFiles(Path bigFile, int maxRows) throws IOException {\n    MappedByteBuffer bb;\n    try(FileChannel in = FileChannel.open(bigFile, READ)) {\n        bb=in.map(FileChannel.MapMode.READ_ONLY, 0, in.size());\n    }\n    for(int start=0, pos=0, end=bb.remaining(), i=1, lineNum=1; pos<end; lineNum++) {\n        while(pos<end && bb.get(pos++)!=\'\\n\');\n        if(lineNum < maxRows && pos<end) continue;\n        Path splitFile = Paths.get(i++ + "split.txt");\n        // if you want to overwrite existing files use CREATE, TRUNCATE_EXISTING\n        try(FileChannel out = FileChannel.open(splitFile, CREATE_NEW, WRITE)) {\n            bb.position(start).limit(pos);\n            while(bb.hasRemaining()) out.write(bb);\n            bb.clear();\n            start=pos;\n            lineNum = 0;\n        }\n    }\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

缺点是它不能与UTF-16orEBCDIC和 等编码一起使用,不像BufferedReader.readLine()它不会\'\\r\'像旧 MacOS9 中那样支持单独的行终止符。

\n\n

此外,它仅支持小于2GB的文件;由于虚拟地址空间有限,32 位 JVM 上的限制可能更小。对于大于限制的文件,有必要逐个迭代源文件的块和map它们。

\n\n

这些问题可以解决,但会增加这种方法的复杂性。考虑到我的机器上速度仅提高了 15% 左右(我没有\xe2\x80\x99 期望更多,因为 I/O 在这里占主导地位),并且当复杂性增加时速度会更小,我不\xe2\ x80\x99t 认为\xe2\x80\x99s 值得。

\n\n
\n\n

最重要的是,对于此任务,Reader/Writer方法就足够了,但您应该注意Charset操作中使用的方法。

\n