Rapidement lu la dernière ligne d'un fichier texte?

Quelle est la manière la plus rapide et la plus efficace de lire la dernière ligne de texte d'un fichier [très, très grand] en Java?

55
demandé sur Gray 2009-03-26 18:17:32

8 réponses

regardez ma réponse à une question similaire pour C# . Le code serait assez similaire, bien que la prise en charge de l'encodage soit quelque peu différente en Java.

fondamentalement, ce n'est pas une chose terriblement facile à faire en général. Comme MSalter le souligne, UTF-8 rend facile de repérer \r ou \n car la représentation UTF-8 de ces caractères est tout à fait la même que celle D'ASCII, et ces octets ne se produiront pas en caractère multi-octets.

donc essentiellement, prendre un tampon de (disons) 2K, et lire progressivement à l'envers (passer à 2K avant que vous étiez avant, lire les 2K suivants) vérifier une fin de ligne. Passez ensuite à la bonne place dans le ruisseau, créer un InputStreamReader sur le haut, et un BufferedReader sur le dessus de cela. Alors appelez BufferedReader.readLine() .

18
répondu Jon Skeet 2017-05-23 10:29:56

ci-dessous sont deux fonctions, l'une qui retourne la dernière ligne non vide d'un fichier sans charger ou pas à travers le fichier entier, et l'autre qui retourne les dernières N lignes du fichier sans pas à travers le fichier entier:

ce que tail fait est zoomer directement sur le dernier caractère du fichier, puis recule, caractère par caractère, enregistrant ce qu'il voit jusqu'à ce qu'il trouve une rupture de ligne. Une fois qu'il trouve une rupture de ligne, il casse en dehors de la boucle. Inverse ce qui a été enregistré et le jette dans une chaîne et revient. 0xA est la nouvelle ligne et 0xD est le retour chariot.

si vos fins de ligne sont \r\n ou crlf ou une autre" double newline style newline", alors vous devrez spécifier n*2 lignes pour obtenir les dernières n Lignes parce qu'il compte 2 lignes pour chaque ligne.

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

Mais vous ne voulez probablement pas la dernière ligne, vous voulez le dernier N lignes, donc utilisez ceci à la place:

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

invoquer les méthodes ci-dessus comme ceci:

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

Avertissement Dans l'Ouest sauvage d'unicode ce code peut causer la sortie de cette fonction de sortir mal. Par exemple "Marie?s" au lieu de "Marie". Caractères avec chapeaux, accents, caractères chinois etc peut causer la sortie à tort parce que les accents sont ajouté comme modificateurs après le caractère. Inverser les caractères composés change la nature de l'identité du caractère lors de l'inversion. Vous aurez à faire batterie complète de tests sur toutes les langues que vous prévoyez d'utiliser cette.

pour plus d'informations sur ce problème d'inversion unicode lire ce: http://msmvps.com/blogs/jon_skeet/archive/2009/11/02/omg-ponies-aka-humanity-epic-fail.aspx

81
répondu Eric Leschinski 2015-03-10 13:29:42

Apache Commons a une implémentation utilisant RandomAccessFile .

ça s'appelle ReversedLinesFileReader .

26
répondu jaco0646 2016-07-15 14:06:18

utiliser FileReader ou FileInputStream ne fonctionnera pas - vous devrez utiliser soit FileChannel ou RandomAccessFile pour boucler le fichier à l'envers à partir de la fin. Les encodages seront un problème, comme Jon l'a dit.

3
répondu Michael Borgwardt 2009-03-26 15:28:14

Dans C# , vous devriez être en mesure de définir les flux de position:

de: 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
répondu rball 2013-03-19 13:43:44

Vous pouvez facilement changer le code ci-dessous pour imprimer la dernière ligne.

MemoryMappedFile pour l'impression des 5 dernières lignes:

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

fichier D'accès aléatoire pour imprimer les 5 dernières lignes:

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
répondu Trying 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
répondu Ajai Singh 2018-04-03 07:49:52

autant que je sache, la manière la plus rapide de lire la dernière ligne d'un fichier texte est D'utiliser la classe FileUtils Apache qui est dans" org.apache.commons.io". J'ai un fichier de deux millions de lignes et en utilisant cette classe, il m'a fallu moins d'une seconde pour trouver la dernière ligne. Voici le code my:

LineIterator lineIterator = FileUtils.lineIterator(newFile(filePath),"UTF-8");
String lastLine="";
while (lineIterator.hasNext()){
 lastLine=  lineIterator.nextLine();
}
0
répondu arash nadali 2018-09-17 04:27:03