Java:读取HUGE文件的最后n行
我想读一个非常大的文件的最后n行,而不使用Java将整个文件读入任何缓冲区/内存区域。
我环顾了JDK API和Apache Commons I / O,无法find适合于此目的的应用程序。
我正在考虑在UNIX中使用tail还是less。 我不认为他们加载整个文件,然后显示文件的最后几行。 在Java中也应该有类似的方法来做同样的事情。
如果你使用的是RandomAccessFile
,你可以使用length
和seek
来到达文件末尾的特定点,然后从那里读取。
如果发现没有足够的线条,请从这一点开始备份并重试。 一旦你知道了第N
行的起始位置,你就可以到那里去阅读和打印。
根据您的数据属性可以进行最初的猜测。 例如,如果是文本文件,则行长度可能不会超过平均值132,因此,要获取最后五行,请在结束之前启动660个字符。 那么,如果你错了,就试试1320(甚至可以使用你从最后660个字符中学到的东西来调整 – 例如:如果这660个字符只是三行,下一个尝试可能是660/3 * 5,加上也许多一点,以防万一)。
我发现这是通过使用Apache Commons-io api中的ReversedLinesFileReader
来完成的最简单的方法。 这个方法会给你从文件的底部到顶部的行,你可以指定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(!object.readLine().isEmpty() && counter < n_lines) { System.out.println(object.readLine()); counter++; }
RandomAccessFile是一个很好的开始,正如其他答案所描述的。 有一个重要的警告,但。
如果您的文件没有使用每字符一个字节的编码进行编码,则readLine()
方法不适用于您。 而readUTF()
在任何情况下都不起作用。 (它读取一个string前面的string…)
相反,您将需要确保您以尊重编码的字符边界的方式查找行尾标记。 对于固定长度的编码(例如UTF-16或UTF-32的版本),您需要从字节位置开始提取字符,这些位置可以被字节大小(字节)整除。 对于可变长度编码(例如UTF-8),您需要search一个字节,该字节必须是字符的第一个字节。
对于UTF-8,字符的第一个字节将是0xxxxxxx
或110xxxxx
或1110xxxx
或11110xxx
。 其他任何内容都是第二个/第三个字节,或者是非法的UTF-8序列。 请参阅Unicode标准5.2版第3.9章表3-7。 这意味着,正如评论讨论指出的那样,正确编码的UTF-8stream中的任何0x0A和0x0D字节将表示LF或CR字符。 因此,计算字节是一个有效的实施策略(对于UTF-8)。
确定了一个合适的字符边界后,就可以调用new String(...)
传递字节数组,偏移量,计数和编码,然后重复调用String.lastIndexOf(...)
来计算行结束String.lastIndexOf(...)
。
我发现RandomAccessFile
和其他缓冲区读取器类太慢了。 没有什么比tail -<#lines>
更快的了tail -<#lines>
。 所以这对我来说是最好的解决scheme。
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(); }
CircularFifoBuffer来自apache的commons。 如何读取.txt文件的最后5行到java中的类似问题的答案
请注意,在Apache Commons Collections 4中,该类似乎已被重命名为CircularFifoQueue
RandomAccessFile
允许寻求(http://download.oracle.com/javase/1.4.2/docs/api/java/io/RandomAccessFile.html)。; File.length
方法将返回文件的大小。 问题是确定行数。 为此,您可以查找文件的结尾并向后读取,直到您点击正确的行数。
这是我发现的最好的方法。 简单而快速,高效的内存。
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(); }
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;