Java - Чтение текстового файла чанками - PullRequest
2 голосов
/ 01 апреля 2011

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

Я завершил чтение каждой строки строки файла с помощью буферизованного считывателя, и я могу создавать куски моего файла с помощью RandomAccessFile в сочетании сMappedByteBuffer, но объединить эти два не просто.

Проблема в том, что фрагмент просто врезается в последнюю строку моего фрагмента.У меня никогда не было всей последней строки моего блока, поэтому обработка этой последней строки журнала невозможна.Я пытаюсь найти способ разрезать мой файл на куски переменной длины, относящиеся к концу строк.

У кого-нибудь есть код для этого?

Ответы [ 2 ]

9 голосов
/ 01 апреля 2011

Вы можете найти смещения в файле, которые находятся на границах строк, прежде чем начинать обработку фрагментов. Начните со смещения, разделив размер файла на номер чанка, и ищите, пока не найдете границу строки. Затем введите эти смещения в ваш многопоточный файловый процессор. Вот полный пример, который использует количество доступных процессоров для количества кусков:

import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class ReadFileByChunks {
    public static void main(String[] args) throws IOException {
        int chunks = Runtime.getRuntime().availableProcessors();
        long[] offsets = new long[chunks];
        File file = new File("your.file");

        // determine line boundaries for number of chunks
        RandomAccessFile raf = new RandomAccessFile(file, "r");
        for (int i = 1; i < chunks; i++) {
            raf.seek(i * file.length() / chunks);

            while (true) {
                int read = raf.read();
                if (read == '\n' || read == -1) {
                    break;
                }
            }

            offsets[i] = raf.getFilePointer();
        }
        raf.close();

        // process each chunk using a thread for each one
        ExecutorService service = Executors.newFixedThreadPool(chunks);
        for (int i = 0; i < chunks; i++) {
            long start = offsets[i];
            long end = i < chunks - 1 ? offsets[i + 1] : file.length();
            service.execute(new FileProcessor(file, start, end));
        }
        service.shutdown();
    }

    static class FileProcessor implements Runnable {
        private final File file;
        private final long start;
        private final long end;

        public FileProcessor(File file, long start, long end) {
            this.file = file;
            this.start = start;
            this.end = end;
        }

        public void run() {
            try {
                RandomAccessFile raf = new RandomAccessFile(file, "r");
                raf.seek(start);

                while (raf.getFilePointer() < end) {
                    String line = raf.readLine();
                    if (line == null) {
                        continue;
                    }

                    // do what you need per line here
                    System.out.println(line);
                }

                raf.close();
            } catch (IOException e) {
                // deal with exception
            }
        }
    }
}
0 голосов
/ 01 апреля 2011

Вы должны позволить своим кускам перекрываться.Если ни одна строка не длиннее блока, то достаточно одного блока.Вы уверены, что вам нужна многопоточная версия?Разве производительность gnu grep недостаточно хороша?

Реализация gnu grep решила проблему со строками, пересекающими границу блока.Если вам не нужна лицензия GNU, вы, возможно, позаимствуете идеи и код оттуда.Это очень эффективная однопоточная реализация.

...