【问题标题】:Does RandomAccessFile in java read entire file in memory?java中的RandomAccessFile是否读取内存中的整个文件?
【发布时间】:2013-03-25 10:24:06
【问题描述】:

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

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

【问题讨论】:

  • 不,seek() 不会将 任何内容 读入内存,更不用说整个文件了。您拥有完全的控制权。
  • 我阅读了那个问题,但我想了解,如果文件是 UTF-8 编码的,那么不鼓励使用 RandomAccessFile 吗?
  • 不同意重复。这更侧重于 RandomAccessFile,而另一个更侧重于应用程序,甚至没有提到 RAF。

标签: java randomaccessfile


【解决方案1】:
  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 识别

【讨论】:

  • 能否在不阅读整个文件的情况下介绍如何使用 BufferedReader?
  • 由于它是逐行读取的,它并没有将整个文件读入内存
  • 我会说,因为它是从头开始逐行读取,所以它会将整个文件读入内存,即使它不会一次加载整个文件。
  • 您能解释一下您在 if 语句中使用的那些数字(10 和 13)是什么意思吗?
  • 这是换行符 - 1 或 2 个字符 \n (10) 和 \r (13) 见 en.wikipedia.org/wiki/Newline
【解决方案2】:

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

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


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

【讨论】:

  • 这是否意味着,一天结束时,我最终会读取内存中的整个文件?
  • 如果你按照我的建议去做,那就不行了。如果你单独使用 BufferedReader,它会读取整个文件,我不建议你这样做。
  • 你能不能分享一下这个新手的sn-p代码:(。我想到达文件末尾,回溯到n行,然后把n行读到我的记忆中
  • 您需要将文件末尾读取为byte[],如果您看到所需的换行符数量,您就知道该跳到哪里了。如果你不这样做,你需要再读一些,直到你这样做。注意:如果文件没有换行符,您最终可能会读取整个文件。我建议你试一试;)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-03-29
  • 1970-01-01
相关资源
最近更新 更多