Szybko odczytać ostatnią linię pliku tekstowego?

Jaki jest najszybszy i najskuteczniejszy sposób odczytania ostatniej linijki tekstu z [bardzo, bardzo dużego] pliku w Javie?

 55
Author: Gray, 2009-03-26

8 answers

Spójrz na moją odpowiedź na podobne pytanie do C # . Kod byłby bardzo podobny, chociaż obsługa kodowania jest nieco inna w Javie.

Zasadniczo nie jest to strasznie łatwa rzecz do zrobienia w ogóle. Jak podkreśla MSalter, UTF - 8 ułatwia wykrycie \r LUB \n, ponieważ reprezentacja UTF-8 tych znaków jest taka sama jak ASCII, a te bajty nie będą występować w postaci wielobajtowej.

Więc zasadniczo, weź bufor (powiedzmy) 2K, i stopniowo Czytaj wstecz (przejdź do 2K zanim byłeś wcześniej, przeczytaj następne 2K) sprawdzanie zakończenia linii. Następnie przejdź do dokładnie odpowiedniego miejsca w strumieniu, Utwórz InputStreamReader na górze i BufferedReader na górze. Więc po prostu zadzwoń BufferedReader.readLine().

 18
Author: Jon Skeet,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 10:29:56

Poniżej znajdują się dwie funkcje, jedna, która zwraca ostatnią niepustą linię pliku bez ładowania lub przechodzenia przez cały plik, a druga, która zwraca Ostatnie N linii pliku bez przechodzenia przez cały plik:

To, co robi tail, to przybliżanie do ostatniego znaku pliku, następnie cofanie się, znak po znaku, nagrywanie tego, co widzi, aż znajdzie przerwę w linii. Po znalezieniu przerwy w linii, wyłamuje się z pętli. Odwraca to, co było nagrane i wrzuca go do łańcucha i powraca. 0xA to nowa linia, a 0XD to powrót karetki.

Jeśli Twoje zakończenia linii to \r\n lub crlf lub jakiś inny "double newline style newline", wtedy będziesz musiał podać n*2 linii, aby uzyskać Ostatnie N linii, ponieważ liczy się 2 linie dla każdej linii.

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 */
            }
    }
}

Ale prawdopodobnie nie chcesz ostatniej linii, chcesz ostatnich n linii, więc użyj tego zamiast:

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) {
            }
    }
}

Wywołaj powyższe metody jak to:

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

Warning Na Dzikim Zachodzie unicode ten kod może spowodować, że wyjście tej funkcji wyjdzie źle. Na przykład "Mary?s "zamiast " Mary". Znaki z kapeluszami, akcentami, chińskimi znakami itp. mogą powodować błędne wyjście, ponieważ akcenty są dodawane jako modyfikatory po znaku. Odwrócenie znaków złożonych zmienia charakter tożsamości znaku przy odwróceniu. Będziesz musiał wykonać pełną baterię testów na wszystkich języki, z którymi planujesz to wykorzystać.

Aby uzyskać więcej informacji na temat tego problemu odwrócenia unicode przeczytaj to: http://msmvps.com/blogs/jon_skeet/archive/2009/11/02/omg-ponies-aka-humanity-epic-fail.aspx

 81
Author: Eric Leschinski,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-03-10 13:29:42

Apache Commons posiada implementację używającą RandomAccessFile .

Nazywa się ReversedLinesFileReader .

 26
Author: jaco0646,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-07-15 14:06:18

Używanie FileReader lub FileInputStream nie będzie działać - będziesz musiał użyć FileChannel lub RandomAccessFile, aby zapętlić Plik od końca. Kodowanie będzie jednak problemem, jak powiedział Jon.

 3
Author: Michael Borgwardt,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-03-26 15:28:14

W C # , Powinieneś być w stanie ustawić pozycję strumienia:

From: 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
    }
}
 0
Author: rball,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-03-19 13:43:44

możesz łatwo zmienić poniższy kod, aby wydrukować ostatnią linię.

MemoryMappedFile do drukowania ostatnich 5 linii:

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 to print last 5 lines:

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;
                }
            }

        }
    }
 0
Author: Trying,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-09-25 11:43:49
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();
}
 0
Author: Ajai Singh,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-04-03 07:49:52

Z tego co wiem najszybszym sposobem na odczytanie ostatniej linijki pliku tekstowego jest użycie klasy Apache FileUtils, która jest w "org.apache.commons.io". mam dwa miliony linii pliku i używając tej klasy, Zajęło mi mniej niż jedną sekundę, aby znaleźć ostatnią linię. Oto Mój kod:

LineIterator lineIterator = FileUtils.lineIterator(newFile(filePath),"UTF-8");
String lastLine="";
while (lineIterator.hasNext()){
 lastLine=  lineIterator.nextLine();
}
 0
Author: arash nadali,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-09-17 04:27:03