Java中的RandomAccessFile会将整个文件读入内存吗?

10

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

想知道最有效的方法是什么。了解了Java中的RandomAccessFile,但是它的seek()方法是否会将整个文件读入内存中。它使用本地实现,所以我无法参考源代码。


不,seek() 不会将 任何 数据读入内存,更不用说整个文件了。你完全可以掌控。 - NPE
我看了那个问题,但我想理解一下,如果文件是UTF-8编码的,那么使用RandomAccessFile是否被反对? - Vinod Jayachandran
1
不同意重复。这个更侧重于RandomAccessFile,而另一个更多关注应用程序,甚至没有提到RAF。 - Ciro Santilli OurBigBook.com
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);
}

它从尾部开始逐字节读取文件,并将其写入ByteArrayOutputStream,当读到LF时,它会反转字节并创建一行。

需要改进的两个方面:

  1. 缓冲

  2. EOL识别


1
你能否包含如何使用BufferedReader而不读取整个文件的内容? - Peter Lawrey
由于它逐行读取,因此它不会将整个文件读入内存。 - Evgeniy Dorofeev
我认为由于它从开头逐行读取,即使它不一次性加载整个文件,它也会将整个文件读入内存。 - Peter Lawrey
你能解释一下你在if语句中使用的那些数字(10和13)是什么意思吗? - kalibrain
它是换行符-1或2个字符\n(10)和\r(13),请参见http://en.wikipedia.org/wiki/Newline。 - Evgeniy Dorofeev

0
如果您需要随机访问,您需要使用RandomAccessFile。如果您知道自己在做什么,可以将从中获取的字节转换为UTF-8。
如果您使用BuffredReader,则可以使用skip(n)按字符数跳过,这意味着它必须读取整个文件。
一种实现此操作的方法是结合使用FileInputStream和skip(),通过读取N个换行符来确定要读取的位置,然后将流包装在BufferedReader中以使用UTF-8编码读取行。

这是否意味着,到了一天结束时,我最终会将整个文件读入内存中? - Vinod Jayachandran
不过如果你按照我的建议去做的话就不会出现这种情况。如果你只使用 BufferedReader,它会读取整个文件,这是我不建议你这样做的原因。 - Peter Lawrey
你能否分享一段代码给这个新手呢 :( 。我想要到达文件末尾,回溯 n 行,然后将这 n 行读入内存。 - Vinod Jayachandran
你需要将文件末尾读取为 byte[],如果你看到所需的换行符数量,你就知道要跳过哪里。如果没有,你需要继续读取直到找到为止。注意:如果文件没有换行符,你可能会读取整个文件。我建议你试一试 ;) - Peter Lawrey

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接