Below are two functions, one of which returns the last non-empty line of a file without loading or iterating over the entire file, and the other, which returns the last N lines of the file without going through the entire file:
Which tail is the zoom right to the last character of the file, then a step back, character by character, recording what it sees until it finds a line break. As soon as it finds a line break, it exits the loop. Pays attention to what was written, and inserts it into a string and returns. 0xA is the new line, and 0xD is the carriage return.
If your lines end with \r\n or crlf or some other “double line new line new line”, you will need to specify n * 2 lines to get the last n lines, because they count 2 lines for each line.
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) { } } }
But you probably don't need the last line, you need the last N lines, so use this instead:
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) { } } }
Call the above methods as follows:
File file = new File("D:\\stuff\\huge.log"); System.out.println(tail(file)); System.out.println(tail2(file, 10));
Caution In the wild west of Unicode, this code may cause an error in the exit of this function. For example, "Mary" instead of "Mary." Symbols with hats, accents, hieroglyphs , etc. They can lead to an incorrect result, because accents are added after the character as modifiers. Inverse composite characters change the character’s identity when reversing. You will need to perform a full test verification in all languages in which you plan to use this.
For more information about this Unicode U-turn issue, read the following: http://msmvps.com/blogs/jon_skeet/archive/2009/11/02/omg-ponies-aka-humanity-epic-fail.aspx