Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Quickly read the last line of a text file?

Tags:

java

file

io

What's the quickest and most efficient way of reading the last line of text from a [very, very large] file in Java?

like image 697
Jake Avatar asked Mar 26 '09 15:03

Jake


People also ask

How do I read the last line of a text file?

var lastLine = File. ReadLines("file. txt"). Last();

How do you read the last line of a text file in C++?

Use seekg to jump to the end of the file, then read back until you find the first newline.

How do you read the last line of a text file in Java?

This snippet should work for you: BufferedReader input = new BufferedReader(new FileReader(fileName)); String last, line; while ((line = input. readLine()) != null) { last = line; } //do something with last!


2 Answers

Below are two functions, one that returns the last non-blank line of a file without loading or stepping through the entire file, and the other that returns the last N lines of the file without stepping through the entire file:

What tail does is zoom straight to the last character of the file, then steps backward, character by character, recording what it sees until it finds a line break. Once it finds a line break, it breaks out of the loop. Reverses what was recorded and throws it into a string and returns. 0xA is the new line and 0xD is the carriage return.

If your line endings are \r\n or crlf or some other "double newline style newline", then you will have to specify n*2 lines to get the last n lines because it counts 2 lines for every 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) {                 /* ignore */             }     } } 

But you probably don't want the last line, you want 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) {             }     } } 

Invoke the above methods like this:

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

Warning In the wild west of unicode this code can cause the output of this function to come out wrong. For example "Mary?s" instead of "Mary's". Characters with hats, accents, Chinese characters etc may cause the output to be wrong because accents are added as modifiers after the character. Reversing compound characters changes the nature of the identity of the character on reversal. You will have to do full battery of tests on all languages you plan to use this with.

For more information about this unicode reversal problem read this: https://codeblog.jonskeet.uk/2009/11/02/omg-ponies-aka-humanity-epic-fail/

like image 117
20 revs, 8 users 83% Avatar answered Sep 20 '22 03:09

20 revs, 8 users 83%


Apache Commons has an implementation using RandomAccessFile.

It's called ReversedLinesFileReader.

like image 42
jaco0646 Avatar answered Sep 20 '22 03:09

jaco0646