我需要从一个大文件中(比如2GB)读取最后的n行。该文件是UTF-8编码的。
想知道最有效的方法是什么。了解了Java中的RandomAccessFile,但是它的seek()方法是否会将整个文件读入内存中。它使用本地实现,所以我无法参考源代码。
我需要从一个大文件中(比如2GB)读取最后的n行。该文件是UTF-8编码的。
想知道最有效的方法是什么。了解了Java中的RandomAccessFile,但是它的seek()方法是否会将整个文件读入内存中。它使用本地实现,所以我无法参考源代码。
RandomAccessFile.seek只是设置文件指针当前位置,不会读取任何字节到内存中。
由于您的文件是UTF-8编码,它是一个文本文件。对于读取文本文件,我们通常使用BufferedReader。Java 7甚至添加了一个方便的方法File.newBufferedReader来创建一个BufferedReader实例,以从文件中读取文本。尽管对于读取最后n行可能效率不高,但易于实现。
为了高效,我们需要使用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时,它会反转字节并创建一行。
需要改进的两个方面:
缓冲
EOL识别
byte[]
,如果你看到所需的换行符数量,你就知道要跳过哪里。如果没有,你需要继续读取直到找到为止。注意:如果文件没有换行符,你可能会读取整个文件。我建议你试一试 ;) - Peter Lawrey
seek()
不会将 任何 数据读入内存,更不用说整个文件了。你完全可以掌控。 - NPE