web-dev-qa-db-fra.com

Lire rapidement 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 volumineux] en Java?

58
Jake

Jetez un oeil à ma réponse à un question similaire pour C # . Le code serait assez similaire, bien que le support d'encodage soit quelque peu différent en Java.

Fondamentalement, ce n'est pas une chose terriblement facile à faire en général. Comme le souligne MSalter, UTF-8 permet de repérer facilement \r Ou \n Car la représentation UTF-8 de ces caractères est identique à ASCII, et ces octets ne se produiront pas en caractères multi-octets.

Donc, fondamentalement, prenez un tampon de (disons) 2K, et lisez progressivement en arrière (passez à 2K avant que vous ne soyez avant, lisez le 2K suivant) en vérifiant la fin d'une ligne. Ensuite, passez exactement au bon endroit dans le flux, créez un InputStreamReader en haut et un BufferedReader en plus. Ensuite, appelez simplement BufferedReader.readLine().

19
Jon Skeet

Voici deux fonctions, l'une qui renvoie la dernière ligne non vide d'un fichier sans charger ou parcourir l'intégralité du fichier, et l'autre qui renvoie les N dernières lignes du fichier sans passer par le fichier entier:

Ce que fait Tail, c'est zoomer directement sur le dernier caractère du fichier, puis reculer, caractère par caractère, en enregistrant ce qu'il voit jusqu'à ce qu'il trouve un saut de ligne. Une fois qu'il trouve un saut de ligne, il sort de la boucle. Inverse ce qui a été enregistré et le jette dans une chaîne et retourne. 0xA est la nouvelle ligne et 0xD est le retour chariot.

Si vos fins de ligne sont \r\n ou crlf ou un autre "double newline style newline", alors vous devrez spécifier n * 2 lignes pour obtenir les n dernières lignes car 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 les N dernières lignes, alors utilisez plutôt ceci:

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

Appelez 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 de l'unicode, ce code peut provoquer une erreur de sortie de cette fonction. Par exemple "Mary's" au lieu de "Mary's". Les caractères avec chapeaux, accents, caractères chinois etc. peuvent entraîner une erreur de sortie car des accents sont ajoutés comme modificateurs après le caractère. L'inversion des caractères composés change la nature de l'identité du personnage lors de l'inversion. Vous devrez effectuer une batterie complète de tests sur toutes les langues avec lesquelles vous prévoyez de l'utiliser.

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

83
Eric Leschinski

Apache Commons a une implémentation utilisant RandomAccessFile .

Cela s'appelle ReversedLinesFileReader .

30
jaco0646

L'utilisation de FileReader ou FileInputStream ne fonctionnera pas - vous devrez utiliser FileChannel ou RandomAccessFile pour parcourir le fichier en arrière depuis la fin. Les encodages seront un problème, comme l'a dit Jon.

4
Michael Borgwardt

autant que je sache La façon la plus rapide de lire la dernière ligne d'un fichier texte est d'utiliser la classe FileUtils Apache qui se trouve 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 mon code:

LineIterator lineIterator = FileUtils.lineIterator(newFile(filePath),"UTF-8");
String lastLine="";
while (lineIterator.hasNext()){
 lastLine=  lineIterator.nextLine();
}
2
arash nadali

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

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

        }
    }
2
Trying
Path path = Paths.get(pathString);
      List<String> allLines = Files.readAllLines(path);
      return allLines.get(allLines.size()-1);
1
user2117229
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();
}
1
Ajai Singh

Dans C # , vous devriez pouvoir définir la position du flux:

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
rball