Наиболее эффективное слияние двух текстовых файлов. - PullRequest
2 голосов
/ 04 октября 2011

Таким образом, у меня есть большие (около 4 гигабайт каждый) текстовые файлы в парах, и мне нужно создать третий файл, который будет состоять из 2 файлов в режиме перемешивания.Следующее уравнение представляет это лучше всего:

3rdfile = (4 строки из файла 1) + (4 строки из файла 2), и это повторяется, пока я не достигну конца файла 1 (оба входных файла будут одинаковымидлина - это по определению).Вот код, который я сейчас использую, но он не очень хорошо масштабируется для больших файлов.Мне было интересно, есть ли более эффективный способ сделать это - поможет ли работа с отображенным файлом памяти?Все идеи приветствуются.

public static void mergeFastq(String forwardFile, String reverseFile, String outputFile) {

    try {
        BufferedReader inputReaderForward = new BufferedReader(new FileReader(forwardFile));
        BufferedReader inputReaderReverse = new BufferedReader(new FileReader(reverseFile));
        PrintWriter outputWriter = new PrintWriter(new FileWriter(outputFile, true));

        String forwardLine = null;
        System.out.println("Begin merging Fastq files");
        int readsMerge = 0;
        while ((forwardLine = inputReaderForward.readLine()) != null) {

            //append the forward file
            outputWriter.println(forwardLine);
            outputWriter.println(inputReaderForward.readLine());
            outputWriter.println(inputReaderForward.readLine());
            outputWriter.println(inputReaderForward.readLine());
            //append the reverse file
            outputWriter.println(inputReaderReverse.readLine());
            outputWriter.println(inputReaderReverse.readLine());
            outputWriter.println(inputReaderReverse.readLine());
            outputWriter.println(inputReaderReverse.readLine());

            readsMerge++;
            if(readsMerge % 10000 == 0) {
                System.out.println("[" + now() + "] Merged 10000");
                readsMerge = 0;
            }

        }

        inputReaderForward.close();
        inputReaderReverse.close();
        outputWriter.close();

    } catch (IOException ex) {
        Logger.getLogger(Utilities.class.getName()).log(Level.SEVERE, "Error while merging FastQ files", ex);
    }
}

Ответы [ 4 ]

2 голосов
/ 04 октября 2011

Может быть, вы также хотите попробовать использовать BufferedWriter для сокращения операций ввода-вывода в вашем файле. http://download.oracle.com/javase/6/docs/api/java/io/BufferedWriter.html

0 голосов
/ 04 октября 2011

Если бы основным требованием была производительность, то я бы закодировал эту функцию на C или C ++ вместо Java.

Но независимо от используемого языка я бы попытался сам управлять памятью. Я хотел бы создать два больших буфера, скажем, 128 МБ или более каждый и заполнить их данными из двух текстовых файлов. Затем вам нужен третий буфер, который в два раза больше двух предыдущих. Алгоритм начнет перемещать символы один за другим из входного буфера # 1 в целевой буфер и в то же время считать EOL. Как только вы достигнете 4-й строки, вы сохраните текущую позицию в этом буфере и повторите тот же процесс со вторым входным буфером. Вы продолжаете чередовать два входных буфера, пополняя буферы, когда вы используете все данные в них. Каждый раз, когда вам нужно заполнить входные буферы, вы также можете записать целевой буфер и очистить его.

0 голосов
/ 04 октября 2011

Буферизуйте ваши операции чтения и записи. Буфер должен быть достаточно большим, чтобы минимизировать операции чтения / записи и при этом эффективно использовать память. Это действительно просто, и это работает.

void write(InputStream is, OutputStream os) throws IOException {
    byte[] buf = new byte[102400]; //optimize the size of buffer to your needs
    int num;
    while((n = is.read(buf)) != -1){
        os.write(buffer, 0, num);
    }
}

EDIT: Я только что понял, что вам нужно перетасовать строки, поэтому этот код не будет работать для вас как есть, но концепция остается прежней.

0 голосов
/ 04 октября 2011

Простой ответ - использовать больший буфер, который помогает сократить общее количество выполняемых вызовов ввода / вывода.

Обычно IO с отображением в памяти с помощью FileChannel (см. Java NIO) используется для обработки ввода-вывода большого файла данных. В этом случае, однако, это не так, так как вам нужно проверить содержимое файла, чтобы определить границу для каждых 4 строк.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...