11

我需要从一个大文件(比如 2GB)中读取最后 n 行。该文件采用 UTF-8 编码。

想知道最有效的方法。阅读 java 中的 RandomAccessFile,但使用 seek() 方法,读取内存中的整个文件。它使用本机实现,所以我无法参考源代码。

4

2 回答 2

6
  1. RandomAccessFile.seek 只是设置文件指针的当前位置,没有字节被读入内存。

  2. 由于您的文件是 UTF-8 编码的,因此它是一个文本文件。对于读取文本文件,我们通常使用 BufferedReader,Java 7 甚至添加了一个方便的方法 File.newBufferedReader 来创建 BufferedReader 的实例来从文件中读取文本。虽然读取最后 n 行可能效率低下,但易于实现。

  3. 为了提高效率,我们需要 RandomAccessFile 并从末尾开始向后读取文件。这是一个基本示例

public static void main(String[] args) throws Exception {
    int n = 3;
    List<String> lines = new ArrayList<>();
    try (RandomAccessFile f = new RandomAccessFile("test", "r")) {
        ByteArrayOutputStream bout = new ByteArrayOutputStream();
        for (long length = f.length(), p = length - 1; p > 0 && lines.size() < n; p--) {
            f.seek(p);
            int b = f.read();
            if (b == 10) {
                if (p < length - 1) {
                    lines.add(0, getLine(bout));
                    bout.reset();
                }
            } else if (b != 13) {
                bout.write(b);
            }
        }
    }
    System.out.println(lines);
}

static String getLine(ByteArrayOutputStream bout) {
    byte[] a = bout.toByteArray();
    // reverse bytes
    for (int i = 0, j = a.length - 1; j > i; i++, j--) {
        byte tmp = a[j];
        a[j] = a[i];
        a[i] = tmp;
    }
    return new String(a);
}

它从tail到ByteArrayOutputStream逐个字节地读取文件,当达到LF时,它会反转字节并创建一行。

有两点需要改进:

  1. 缓冲

  2. EOL 识别

于 2013-03-25T10:30:13.570 回答
0

如果您需要随机访问,则需要 RandomAccessFile。如果您知道自己在做什么,则可以将从此获得的字节转换为 UTF-8。

如果您使用 BuffredReader,您可以按字符数使用 skip(n),这意味着它必须读取整个文件。


一种组合方式;就是使用带有skip()的FileInputStream,通过回读N个换行符找到你想要读取的位置,然后将流包装在BufferedReader中以读取UTF-8编码的行。

于 2013-03-25T10:34:46.967 回答