快速阅读文本文件的最后一行?

从Java中非常大的文件中读取最后一行文本的最快速和最有效的方法是什么?

看看我对C#的类似问题的答案。 代码会非常相似,尽pipeJava中的编码支持有些不同。

一般来说做基本上不是一件容易的事情。 正如MSalter指出的那样,UTF-8确实可以很容易地识别\r\n因为这些字符的UTF-8表示与ASCII相同,而且这些字节不会以多字节字符出现。

所以基本上,取一个(比如说)2K的缓冲区,然后逐步读回(在你之前跳到2K,读下一个2K),检查一个行终止。 然后跳到stream中的正确位置,在顶部创build一个InputStreamReader ,并在其上创build一个BufferedReader 。 然后调用BufferedReader.readLine()

下面是两个函数,一个返回一个文件的最后一个非空行而不加载或者遍历整个文件,另一个返回文件的最后N行,而不通过整个文件:

尾部会直接缩放到文件的最后一个字符,然后逐个字符逐个后退,logging所看到的内容,直到find换行符。 一旦find换行符,就会跳出循环。 将所logging的内容颠倒并将其引发到一个string中并返回。 0xA是新行,0xD是回车。

如果你的行尾是\r\ncrlf或者其他一些“double newline style newline”,那么你将不得不指定n * 2行来获得最后n行,因为它对每行都计算了2行。

 public String tail( File file ) { RandomAccessFile fileHandler = null; try { fileHandler = new RandomAccessFile( file, "r" ); long fileLength = fileHandler.length() - 1; StringBuilder sb = new StringBuilder(); for(long filePointer = fileLength; filePointer != -1; filePointer--){ fileHandler.seek( filePointer ); int readByte = fileHandler.readByte(); if( readByte == 0xA ) { if( filePointer == fileLength ) { continue; } break; } else if( readByte == 0xD ) { if( filePointer == fileLength - 1 ) { continue; } break; } sb.append( ( char ) readByte ); } String lastLine = sb.reverse().toString(); return lastLine; } catch( java.io.FileNotFoundException e ) { e.printStackTrace(); return null; } catch( java.io.IOException e ) { e.printStackTrace(); return null; } finally { if (fileHandler != null ) try { fileHandler.close(); } catch (IOException e) { /* ignore */ } } } 

但是你可能不想要最后一行,你想要最后的N行,所以用这个代替:

 public String tail2( File file, int lines) { java.io.RandomAccessFile fileHandler = null; try { fileHandler = new java.io.RandomAccessFile( file, "r" ); long fileLength = fileHandler.length() - 1; StringBuilder sb = new StringBuilder(); int line = 0; for(long filePointer = fileLength; filePointer != -1; filePointer--){ fileHandler.seek( filePointer ); int readByte = fileHandler.readByte(); if( readByte == 0xA ) { if (filePointer < fileLength) { line = line + 1; } } else if( readByte == 0xD ) { if (filePointer < fileLength-1) { line = line + 1; } } if (line >= lines) { break; } sb.append( ( char ) readByte ); } String lastLine = sb.reverse().toString(); return lastLine; } catch( java.io.FileNotFoundException e ) { e.printStackTrace(); return null; } catch( java.io.IOException e ) { e.printStackTrace(); return null; } finally { if (fileHandler != null ) try { fileHandler.close(); } catch (IOException e) { } } } 

调用上面的方法:

 File file = new File("D:\\stuff\\huge.log"); System.out.println(tail(file)); System.out.println(tail2(file, 10)); 

警告在unicode的狂野西部,这个代码可能会导致这个函数的输出出错。 例如“Mary's”而不是“Mary's”。 带有帽子,口音,汉字等的字符可能会导致输出错误,因为在字符之后添加了重音作为修饰符。 反转复合字符改变反转时angular色身份的性质。 你将不得不对你打算使用的所有语言进行全面的testing。

有关此unicode逆转问题的更多信息,请阅读以下内容: http : //msmvps.com/blogs/jon_skeet/archive/2009/11/02/omg-ponies-aka-humanity-epic-fail.aspx

Apache Commons有一个使用RandomAccessFile的实现。

它被称为ReversedLinesFileReader 。

使用FileReader或FileInputStream将不起作用 – 您将不得不使用FileChannel或RandomAccessFile从最后向后循环文件。 正如乔恩所说,编码将成为一个问题。

C#中 ,您应该能够设置stream的位置:

来自: http : //bytes.com/groups/net-c/269090-streamreader-read-last-line-text-file

 using(FileStream fs = File.OpenRead("c:\\file.dat")) { using(StreamReader sr = new StreamReader(fs)) { sr.BaseStream.Position = fs.Length - 4; if(sr.ReadToEnd() == "DONE") // match } } 

您可以轻松地更改下面的代码来打印最后一行。

MemoryMappedFile用于打印最后5行:

 private static void printByMemoryMappedFile(File file) throws FileNotFoundException, IOException{ FileInputStream fileInputStream=new FileInputStream(file); FileChannel channel=fileInputStream.getChannel(); ByteBuffer buffer=channel.map(FileChannel.MapMode.READ_ONLY, 0, channel.size()); buffer.position((int)channel.size()); int count=0; StringBuilder builder=new StringBuilder(); for(long i=channel.size()-1;i>=0;i--){ char c=(char)buffer.get((int)i); builder.append(c); if(c=='\n'){ if(count==5)break; count++; builder.reverse(); System.out.println(builder.toString()); builder=null; builder=new StringBuilder(); } } channel.close(); } 

RandomAccessFile打印最后5行:

 private static void printByRandomAcessFile(File file) throws FileNotFoundException, IOException{ RandomAccessFile randomAccessFile = new RandomAccessFile(file, "r"); int lines = 0; StringBuilder builder = new StringBuilder(); long length = file.length(); length--; randomAccessFile.seek(length); for(long seek = length; seek >= 0; --seek){ randomAccessFile.seek(seek); char c = (char)randomAccessFile.read(); builder.append(c); if(c == '\n'){ builder = builder.reverse(); System.out.println(builder.toString()); lines++; builder = null; builder = new StringBuilder(); if (lines == 5){ break; } } } } 

这是我做到这一点的方式:)希望帮助

  try( BufferedReader reader = new BufferedReader(new FileReader(reqFile))){ String line = null; System.out.println("======================================"); line = reader.readLine(); //Read Line ONE line = reader.readLine(); //Read Line TWO System.out.println("first line : " + line); //Length of one line if lines are of even length int len = line.length(); //skip to the end - 3 lines reader.skip((reqFile.length() - (len*3))); //Searched to the last line for the date I was looking for. while((line = reader.readLine()) != null){ System.out.println("FROM LINE : " + line); String date = line.substring(0,line.indexOf(",")); System.out.println("DATE : " + date); //BAM!!!!!!!!!!!!!! } System.out.println(reqFile.getName() + " Read(" + reqFile.length()/(1000) + "KB)"); System.out.println("======================================"); } catch (IOException x){ x.printStackTrace(); }