2014-08-28 2 views
4

Я хочу объединить огромные файлы, содержащие строки, в один файл и попытался использовать 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 без загрузки всего файла в память?

ответ

13

Если вы хотите объединить два или более файла эффективно, спросите себя, почему вы используете char на основе Reader и Writer для выполнения этой задачи.

Используя эти классы, вы выполняете преобразование байтов файла в символы из кодировки по умолчанию системы в Юникод и обратно из Юникода в кодировку по умолчанию в системе. Это означает, что программа должна выполнить два преобразования данных во всех файлах.

И, между прочим, 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."); 
    } 
} 
+0

Вау, это решение действительно здорово - и исходный код настолько короток. Благодаря! Знаете ли вы решение на основе nio2 для SPLITTING A LARGE FILE в набор меньших файлов? На самом деле, я использую что-то вроде этого: http://todayguesswhat.blogspot.de/2014/05/java-split-large-file-sample-code-high.html. – nimo23

+0

@ nimo23: ну, я думаю, когда вы пытаетесь понять код моего ответа, особенно то, что ['FileChannel.transferTo'] (http://docs.oracle.com/javase/7/docs/api/java/ nio/channels/FileChannel.html # transferTo (long,% 20long,% 20java.nio.channels.WritableByteChannel)), вы поймете, как может выглядеть решение для расщепления (читайте: очень похоже). Если у вас есть трудности с его внедрением, вы можете открыть новый вопрос. – Holger

+0

Хорошо, я попробую его самостоятельно и предоставит вам решение! – nimo23

2

С

Files.newBufferedReader(file).readLine() 

создать новый буфер каждый раз, и он получает всегда сбрасывается в первой строке.

Заменить

BufferedReader reader = Files.newBufferedReader(file); 
while ((line = reader.readLine()) != null) { 
    writer.write(line); 
} 

и .close() читателя, когда сделано.

+0

спасибо, я внесла изменения в исходный код. Знаете ли вы, как я могу сохранить формат объединенных файлов в «mergedFile» -File? Например, объединенные файлы имеют возврат каретки или пустые строки. При использовании вышеописанного метода все это не копируется в «mergedFile». – nimo23

+0

Не знаете, что вы имеете в виду, но можете добавить новую строку вручную, используя writer.write (System.lineSeparator()); –

+0

Да, теперь это работает. Я изменил источник выше. – nimo23

1

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 в начале файла.

Смежные вопросы