我需要从大文件中读取最后n行(比如说2GB)。该文件是UTF-8编码的。
想知道最有效的方法。在java中读取RandomAccessFile,但是seek()方法读取内存中的整个文件。它使用本机实现,因此我无法引用源代码。
答案 0 :(得分:6)
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);
}
它读取从tail到ByteArrayOutputStream的字节之后的文件字节,当达到LF时,它会反转字节并创建一行。
有两件事需要改进:
缓冲
EOL识别
答案 1 :(得分:0)
如果您需要随机访问,则需要RandomAccessFile。如果您知道自己在做什么,可以将从中获得的字节转换为UTF-8。
如果你使用BuffredReader,你可以使用skip(n)字符数,这意味着它必须读取整个文件。
组合执行此操作的方法;是使用带有skip()的FileInputStream,通过回读N个换行符找到要读取的位置,然后将流包装在BufferedReader中以读取UTF-8编码的行。