我想读取一个非常大的文件的最后n行,而不是使用Java将整个文件读入任何缓冲区/内存区域。
我查看了JDK API和Apache Commons I / O,但无法找到适合此目的的那个。
我在考虑在UNIX中使用tail或更少的方式。我不认为他们加载整个文件,然后显示该文件的最后几行。在Java中应该有类似的方法来做同样的事情。
答案 0 :(得分:27)
如果您使用RandomAccessFile
,则可以使用length
和seek
到达文件末尾附近的特定点,然后从那里向前阅读。
如果您发现没有足够的线路,请从该点备份并重试。一旦你弄清楚N
最后一行的开始位置,就可以在那里找到并只是阅读和打印。
可以根据您的数据属性进行初始最佳猜测假设。例如,如果它是一个文本文件,则行长度可能不会超过132的平均值,因此,要获得最后五行,请在结束前开始660个字符。然后,如果你错了,请在1320再试一次(你甚至可以使用你从最后660个字符中学到的东西来调整 - 例如:如果这660个字符只有三行,下一次尝试可能是660/3 * 5,加上可能有点额外的以防万一。)
答案 1 :(得分:27)
我发现使用ReversedLinesFileReader
api中的apache commons-io是最简单的方法。
此方法将为您提供从文件的底部到顶部的行,您可以指定n_lines
值以指定行数。
import org.apache.commons.io.input.ReversedLinesFileReader;
File file = new File("D:\\file_name.xml");
int n_lines = 10;
int counter = 0;
ReversedLinesFileReader object = new ReversedLinesFileReader(file);
while(counter < n_lines) {
System.out.println(object.readLine());
counter++;
}
答案 2 :(得分:19)
RandomAccessFile是一个很好的起点,如其他答案所述。但是有一个重要的警告。
如果您的文件没有使用每个字符一个字节的编码进行编码,则readLine()
方法不适用于您。并且readUTF()
在任何情况下都不起作用。 (它读取一个以字符数开头的字符串......)
相反,您需要确保以一种尊重编码字符边界的方式查找行尾标记。对于固定长度编码(例如UTF-16或UTF-32的风格),您需要从字节位置开始提取字符,这些字符位置可以按字节大小整除。对于可变长度编码(例如UTF-8),您需要搜索必须是字符的第一个字节的字节。
对于UTF-8,字符的第一个字节为0xxxxxxx
或110xxxxx
或1110xxxx
或11110xxx
。其他任何东西都是第二个/第三个字节,或者是非法的UTF-8序列。见The Unicode Standard, Version 5.2, Chapter 3.9,表3-7。这意味着,正如评论讨论所指出的,正确编码的UTF-8流中的任何0x0A和0x0D字节将表示LF或CR字符。因此,如果我们可以假设不使用其他类型的Unicode行分隔符(0x2028,0x2029和0x0085),那么简单地计算0x0A和0x0D字节是一种有效的实现策略(对于UTF-8)。你不能假设,那么代码会更复杂。
确定了正确的字符边界后,您可以调用new String(...)
传递字节数组,偏移量,计数和编码,然后重复调用String.lastIndexOf(...)
来计算行尾。
答案 3 :(得分:4)
我发现RandomAccessFile
和其他Buffer Reader类对我来说太慢了。没有什么能比tail -<#lines>
更快。所以这对我来说是最好的解决方案。
public String getLastNLogLines(File file, int nLines) {
StringBuilder s = new StringBuilder();
try {
Process p = Runtime.getRuntime().exec("tail -"+nLines+" "+file);
java.io.BufferedReader input = new java.io.BufferedReader(new java.io.InputStreamReader(p.getInputStream()));
String line = null;
//Here we first read the next line into the variable
//line and then check for the EOF condition, which
//is the return value of null
while((line = input.readLine()) != null){
s.append(line+'\n');
}
} catch (java.io.IOException e) {
e.printStackTrace();
}
return s.toString();
}
答案 4 :(得分:2)
CircularFifoBuffer。在How to read last 5 lines of a .txt file into java
回答类似的问题请注意,在Apache Commons Collections 4中,此类似乎已重命名为CircularFifoQueue
答案 5 :(得分:1)
RandomAccessFile
允许搜索(http://download.oracle.com/javase/1.4.2/docs/api/java/io/RandomAccessFile.html)。 File.length
方法将返回文件的大小。问题是确定行数。为此,您可以寻找文件的末尾并向后阅读,直到您达到正确的行数。
答案 6 :(得分:1)
ReversedLinesFileReader
可以在Apache Commons IO java库中找到。
int n_lines = 1000;
ReversedLinesFileReader object = new ReversedLinesFileReader(new File(path));
String result="";
for(int i=0;i<n_lines;i++){
String line=object.readLine();
if(line==null)
break;
result+=line;
}
return result;
答案 7 :(得分:1)
我有类似的问题,但我不理解另一种解决方案。
我用过这个。我希望这是简单的代码。
// String filePathName = (direction and file name).
File f = new File(filePathName);
long fileLength = f.length(); // Take size of file [bites].
long fileLength_toRead = 0;
if (fileLength > 2000) {
// My file content is a table, I know one row has about e.g. 100 bites / characters.
// I used 1000 bites before file end to point where start read.
// If you don't know line length, use @paxdiablo advice.
fileLength_toRead = fileLength - 1000;
}
try (RandomAccessFile raf = new RandomAccessFile(filePathName, "r")) { // This row manage open and close file.
raf.seek(fileLength_toRead); // File will begin read at this bite.
String rowInFile = raf.readLine(); // First readed line usualy is not whole, I needn't it.
rowInFile = raf.readLine();
while (rowInFile != null) {
// Here I can readed lines (rowInFile) add to String[] array or ArriyList<String>.
// Later I can work with rows from array - last row is sometimes empty, etc.
rowInFile = raf.readLine();
}
}
catch (IOException e) {
//
}
答案 8 :(得分:1)
package com.uday;
import java.io.File;
import java.io.RandomAccessFile;
public class TailN {
public static void main(String[] args) throws Exception {
long startTime = System.currentTimeMillis();
TailN tailN = new TailN();
File file = new File("/Users/udakkuma/Documents/workspace/uday_cancel_feature/TestOOPS/src/file.txt");
tailN.readFromLast(file);
System.out.println("Execution Time : " + (System.currentTimeMillis() - startTime));
}
public void readFromLast(File file) throws Exception {
int lines = 3;
int readLines = 0;
StringBuilder builder = new StringBuilder();
try (RandomAccessFile randomAccessFile = new RandomAccessFile(file, "r")) {
long fileLength = file.length() - 1;
// Set the pointer at the last of the file
randomAccessFile.seek(fileLength);
for (long pointer = fileLength; pointer >= 0; pointer--) {
randomAccessFile.seek(pointer);
char c;
// read from the last, one char at the time
c = (char) randomAccessFile.read();
// break when end of the line
if (c == '\n') {
readLines++;
if (readLines == lines)
break;
}
builder.append(c);
fileLength = fileLength - pointer;
}
// Since line is read from the last so it is in reverse order. Use reverse
// method to make it correct order
builder.reverse();
System.out.println(builder.toString());
}
}
}
答案 9 :(得分:0)
这是我发现的最佳方式。简单,快速,内存效率高。
public static void tail(File src, OutputStream out, int maxLines) throws FileNotFoundException, IOException {
BufferedReader reader = new BufferedReader(new FileReader(src));
String[] lines = new String[maxLines];
int lastNdx = 0;
for (String line=reader.readLine(); line != null; line=reader.readLine()) {
if (lastNdx == lines.length) {
lastNdx = 0;
}
lines[lastNdx++] = line;
}
OutputStreamWriter writer = new OutputStreamWriter(out);
for (int ndx=lastNdx; ndx != lastNdx-1; ndx++) {
if (ndx == lines.length) {
ndx = 0;
}
writer.write(lines[ndx]);
writer.write("\n");
}
writer.flush();
}
答案 10 :(得分:0)
这是工作所在。
private static void printLastNLines(String filePath, int n) {
File file = new File(filePath);
StringBuilder builder = new StringBuilder();
try {
RandomAccessFile randomAccessFile = new RandomAccessFile(filePath, "r");
long pos = file.length() - 1;
randomAccessFile.seek(pos);
for (long i = pos - 1; i >= 0; i--) {
randomAccessFile.seek(i);
char c = (char) randomAccessFile.read();
if (c == '\n') {
n--;
if (n == 0) {
break;
}
}
builder.append(c);
}
builder.reverse();
System.out.println(builder.toString());
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}