2

我想读取不同块的日志文件以使其成为多线程。该应用程序将在具有多个硬盘的服务器端环境中运行。读入块后,应用程序将处理每个块的每一行。

我已经使用缓冲读取器完成了对每个文件行的读取,并且我可以使用 RandomAccessFile 和 MappedByteBuffer 来制作我的文件块,但是将这两者结合起来并不容易。

问题是该块只是切入我的块的最后一行。我从来没有我的块的最后一行,所以处理最后一行是不可能的。我正在尝试找到一种方法将我的文件切割成关于行尾的可变长度块。

有没有人有这样做的代码?

4

2 回答 2

9

在开始处理块之前,您可以在文件中找到位于行边界的偏移量。从偏移量开始,将文件大小除以块号并搜索直到找到行边界。然后将这些偏移量输入到您的多线程文件处理器中。这是一个完整的示例,它使用可用处理器的数量来表示块的数量:

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
            }
        }
    }
}
于 2011-04-01T09:45:18.810 回答
0

你需要让你的块重叠。如果没有行长于一个块,则一个块重叠就足够了。您确定需要多线程版本吗?gnu grep 性能不够好?

gnu grep 的实现已经解决了跨越块边界的行的问题。如果您不关心 GNU 许可证,您可能可以从那里借用想法和代码。这是一个非常高效的单线程实现。

于 2011-04-01T09:08:42.680 回答