How to read file from end to start (in reverse order) in Java?

I had the same problem as described here. I want to look at lines in file in reverse order, from the end back to the start (The unix tac command will do it).

However my input files are fairly large so reading the whole file into memory, as in the other examples was not really a workable option for me.

Below is the class I came up with, it does use RandomAccessFile, but does not need any buffers, since it just retains pointers to the file itself, and works with the standard InputStream methods.

It works for my cases, and empty files and a few other things I’ve tried. Now I don’t have Unicode characters or anything fancy, but as long as the lines are delimited by LF, and even if they have a LF + CR it should work.

Basic Usage is :

in = new BufferedReader (new InputStreamReader (new ReverseLineInputStream(file)));

while(true) {
    String line = in.readLine();
    if (line == null) {
        break;
    }
    System.out.println("X:" + line);
}

Here is the main source:

package www.kosoft.util;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.RandomAccessFile;

public class ReverseLineInputStream extends InputStream {

    RandomAccessFile in;

    long currentLineStart = -1;
    long currentLineEnd = -1;
    long currentPos = -1;
    long lastPosInFile = -1;

    public ReverseLineInputStream(File file) throws FileNotFoundException {
        in = new RandomAccessFile(file, "r");
        currentLineStart = file.length();
        currentLineEnd = file.length();
        lastPosInFile = file.length() -1;
        currentPos = currentLineEnd; 
    }

    public void findPrevLine() throws IOException {

        currentLineEnd = currentLineStart; 

        // There are no more lines, since we are at the beginning of the file and no lines.
        if (currentLineEnd == 0) {
            currentLineEnd = -1;
            currentLineStart = -1;
            currentPos = -1;
            return; 
        }

        long filePointer = currentLineStart -1;

         while ( true) {
             filePointer--;

            // we are at start of file so this is the first line in the file.
            if (filePointer < 0) {  
                break; 
            }

            in.seek(filePointer);
            int readByte = in.readByte();

            // We ignore last LF in file. search back to find the previous LF.
            if (readByte == 0xA && filePointer != lastPosInFile ) {   
                break;
            }
         }
         // we want to start at pointer +1 so we are after the LF we found or at 0 the start of the file.   
         currentLineStart = filePointer + 1;
         currentPos = currentLineStart;
    }

    public int read() throws IOException {

        if (currentPos < currentLineEnd ) {
            in.seek(currentPos++);
            int readByte = in.readByte();
            return readByte;

        }
        else if (currentPos < 0) {
            return -1;
        }
        else {
            findPrevLine();
            return read();
        }
    }
}

Leave a Comment