web-dev-qa-db-fra.com

Comment lire le fichier de bout en bout (en ordre inverse) en Java?

Je veux lire le fichier dans le sens opposé du début à la fin du fichier,

[1322110800] LOG ROTATION: DAILY
[1322110800] LOG VERSION: 2.0
[1322110800] CURRENT Host STATE:arsalan.hussain;DOWN;HARD;1;CRITICAL - Host Unreachable (192.168.1.107)
[1322110800] CURRENT Host STATE: localhost;UP;HARD;1;PING OK - Packet loss = 0%, RTA = 0.06 ms
[1322110800] CURRENT Host STATE: musewerx-72c7b0;UP;HARD;1;PING OK - Packet loss = 0%, RTA = 0.27 ms

j'utilise le code pour le lire de cette manière,

String strpath="/var/nagios.log";
FileReader fr = new FileReader(strpath);
BufferedReader br = new BufferedReader(fr);
String ch;
int time=0;
String Conversion="";
do {
    ch = br.readLine();
    out.print(ch+"<br/>"); 
} while (ch != null);
fr.close();

Je préférerais lire dans l'ordre inverse en utilisant un lecteur de tampon

17
Salman Raza

Autant que je sache, vous essayez de lire ligne par ligne, vers l’arrière… .. Supposons que c’est le fichier que vous essayez de lire:

ligne 1
ligne 2
line3

Et vous voulez l'écrire dans le flux de sortie du servlet comme suit:

line3
ligne 2
ligne 1

Le code suivant peut être utile dans ce cas:

    List<String> tmp = new ArrayList<String>();

    do {
        ch = br.readLine();
        tmp.add(ch);
        out.print(ch+"<br/>"); 
    } while (ch != null);

    for(int i=tmp.size()-1;i>=0;i--) {
        out.print(tmp.get(i)+"<br/>");
    }
4
anilsinaci

J'ai eu le même problème que décrit ici. Je veux regarder les lignes dans le fichier dans l'ordre inverse, de la fin au début (la commande unix le fera). 

Cependant, mes fichiers d'entrée étant assez volumineux, lire le fichier entier en mémoire, comme dans les autres exemples, n'était pas vraiment une option viable pour moi.

Ci-dessous, la classe que j'ai créée. Elle utilise RandomAccessFile, mais ne nécessite pas de mémoire tampon, car elle conserve uniquement les pointeurs sur le fichier lui-même et fonctionne avec les méthodes standard InputStream

Cela fonctionne pour mes cas, et les fichiers vides et quelques autres choses que j'ai essayées. Maintenant, je n'ai pas de caractères Unicode ni rien d'extraordinaire, mais tant que les lignes sont délimitées par LF, et même si elles ont un LF + CR, cela devrait fonctionner.

L'utilisation de base est: 

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

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

Voici la source principale: 

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();
        }
    }
}
53
Mark O'Donohue

Apache Commons IO a la classe ReversedLinesFileReader pour cela maintenant (depuis la version 2.2). 

Donc, votre code pourrait être:

String strpath="/var/nagios.log";
ReversedLinesFileReader fr = new ReversedLinesFileReader(new File(strpath));
String ch;
int time=0;
String Conversion="";
do {
    ch = fr.readLine();
    out.print(ch+"<br/>"); 
} while (ch != null);
fr.close();
16
nessa.gp

Le ReverseLineInputStream posté ci-dessus est exactement ce que je cherchais. Les fichiers que je lis sont volumineux et ne peuvent pas être mis en mémoire tampon.

Il y a quelques bugs:

  • Le fichier n'est pas fermé
  • si la dernière ligne n'est pas terminée, les 2 dernières lignes sont renvoyées à la première lecture.

Voici le code corrigé:

package www.kosoft.util;

import Java.io.File;
import Java.io.FileNotFoundException;
import Java.io.IOException;
import Java.io.InputStream;
import Java.io.RandomAccessFile;

public class ReverseLineInputStream extends InputStream {

    RandomAccessFile in;

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


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

    }

    private void findPrevLine() throws IOException {
        if (lastChar == -1) {
            in.seek(lastPosInFile);
            lastChar = in.readByte();
        }

        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 > lastPosInFile && currentLineStart < currentLineEnd) {
            // last line in file (first returned)
            findPrevLine();
            if (lastChar != '\n' && lastChar != '\r') {
                // last line is not terminated
                return '\n';
            } else {
                return read();
            }
        } else if (currentPos < 0) {
            return -1;
        } else {
            findPrevLine();
            return read();
        }
    }

    @Override
    public void close() throws IOException {
        if (in != null) {
            in.close();
            in = null;
        }
    }
}
10
Tim Lavallee

Le programme ReverseLineInputStream proposé fonctionne vraiment lentement lorsque vous essayez de lire des milliers de lignes. Sur mon PC Intel Core i7 sur lecteur SSD , environ 60 000 lignes en 80 secondes. Voici la version optimisée inspirée avec lecture tamponnée (opposée à une lecture octet par octet dans ReverseLineInputStream). Le fichier journal de 60 000 lignes est lu en 400 millisecondes:

public class FastReverseLineInputStream extends InputStream {

private static final int MAX_LINE_BYTES = 1024 * 1024;

private static final int DEFAULT_BUFFER_SIZE = 1024 * 1024;

private RandomAccessFile in;

private long currentFilePos;

private int bufferSize;
private byte[] buffer;
private int currentBufferPos;

private int maxLineBytes;
private byte[] currentLine;
private int currentLineWritePos = 0;
private int currentLineReadPos = 0;
private boolean lineBuffered = false;

public ReverseLineInputStream(File file) throws IOException {
    this(file, DEFAULT_BUFFER_SIZE, MAX_LINE_BYTES);
}

public ReverseLineInputStream(File file, int bufferSize, int maxLineBytes) throws IOException {
    this.maxLineBytes = maxLineBytes;
    in = new RandomAccessFile(file, "r");
    currentFilePos = file.length() - 1;
    in.seek(currentFilePos);
    if (in.readByte() == 0xA) {
        currentFilePos--;
    }
    currentLine = new byte[maxLineBytes];
    currentLine[0] = 0xA;

    this.bufferSize = bufferSize;
    buffer = new byte[bufferSize];
    fillBuffer();
    fillLineBuffer();
}

@Override
public int read() throws IOException {
    if (currentFilePos <= 0 && currentBufferPos < 0 && currentLineReadPos < 0) {
        return -1;
    }

    if (!lineBuffered) {
        fillLineBuffer();
    }


    if (lineBuffered) {
        if (currentLineReadPos == 0) {
            lineBuffered = false;
        }
        return currentLine[currentLineReadPos--];
    }
    return 0;
}

private void fillBuffer() throws IOException {
    if (currentFilePos < 0) {
        return;
    }

    if (currentFilePos < bufferSize) {
        in.seek(0);
        in.read(buffer);
        currentBufferPos = (int) currentFilePos;
        currentFilePos = -1;
    } else {
        in.seek(currentFilePos);
        in.read(buffer);
        currentBufferPos = bufferSize - 1;
        currentFilePos = currentFilePos - bufferSize;
    }
}

private void fillLineBuffer() throws IOException {
    currentLineWritePos = 1;
    while (true) {

        // we've read all the buffer - need to fill it again
        if (currentBufferPos < 0) {
            fillBuffer();

            // nothing was buffered - we reached the beginning of a file
            if (currentBufferPos < 0) {
                currentLineReadPos = currentLineWritePos - 1;
                lineBuffered = true;
                return;
            }
        }

        byte b = buffer[currentBufferPos--];

        // \n is found - line fully buffered
        if (b == 0xA) {
            currentLineReadPos = currentLineWritePos - 1;
            lineBuffered = true;
            break;

            // just ignore \r for now
        } else if (b == 0xD) {
            continue;
        } else {
            if (currentLineWritePos == maxLineBytes) {
                throw new IOException("file has a line exceeding " + maxLineBytes
                        + " bytes; use constructor to pickup bigger line buffer");
            }

            // write the current line bytes in reverse order - reading from
            // the end will produce the correct line
            currentLine[currentLineWritePos++] = b;
        }
    }
}}
9
dpetruha
@Test
public void readAndPrintInReverseOrder() throws IOException {

    String path = "src/misctests/test.txt";

    BufferedReader br = null;

    try {
        br = new BufferedReader(new FileReader(path));
        Stack<String> lines = new Stack<String>();
        String line = br.readLine();
        while(line != null) {
            lines.Push(line);
            line = br.readLine();
        }

        while(! lines.empty()) {
            System.out.println(lines.pop());
        }

    } finally {
        if(br != null) {
            try {
                br.close();   
            } catch(IOException e) {
                // can't help it
            }
        }
    }
}

Notez que ce code lit le fichier de trous dans la mémoire puis commence à l’imprimer. C’est la seule façon de procéder avec un lecteur protégé ou un autre lecteur qui ne prend pas en charge la recherche. N'oubliez pas ceci: dans votre cas, vous souhaitez lire un fichier journal, celui-ci peut être très volumineux!

Si vous voulez lire ligne par ligne et imprimer à la volée, vous n’avez pas d’autre choix que d’utiliser un lecteur prenant en charge la recherche telle que Java.io.RandomAccessFile et cela, mais tout sauf trivial. 

2
A4L

J'ai eu un problème avec votre solution @dpetruha pour cette raison:

RandomAccessFile.read () du fichier local garantit-il que le nombre exact d'octets sera lu?

Voici ma solution: (changé seulement fillBuffer)

import Java.io.File;
import Java.io.IOException;
import Java.io.InputStream;
import Java.io.RandomAccessFile;

public class ReverseLineInputStream extends InputStream {

    private static final int MAX_LINE_BYTES = 1024 * 1024;
    private static final int DEFAULT_BUFFER_SIZE = 1024 * 1024;

    private RandomAccessFile in;
    private long currentFilePos;
    private int bufferSize;
    private byte[] buffer;
    private int currentBufferPos;
    private int maxLineBytes;
    private byte[] currentLine;
    private int currentLineWritePos = 0;
    private int currentLineReadPos = 0;
    private boolean lineBuffered = false;

    public ReverseLineInputStream(File file) throws IOException {
        this(file, DEFAULT_BUFFER_SIZE, MAX_LINE_BYTES);
    }

    public ReverseLineInputStream(File file, int bufferSize, int maxLineBytes) throws IOException {
        this.maxLineBytes = maxLineBytes;
        in = new RandomAccessFile(file, "r");
        currentFilePos = file.length() - 1;
        in.seek(currentFilePos);
        if (in.readByte() == 0xA) {
            currentFilePos--;
        }
        currentLine = new byte[maxLineBytes];
        currentLine[0] = 0xA;

        this.bufferSize = bufferSize;
        buffer = new byte[bufferSize];
        fillBuffer();
        fillLineBuffer();
    }

    @Override
    public int read() throws IOException {
        if (currentFilePos <= 0 && currentBufferPos < 0 && currentLineReadPos < 0) {
            return -1;
        }

        if (!lineBuffered) {
            fillLineBuffer();
        }

        if (lineBuffered) {
            if (currentLineReadPos == 0) {
                lineBuffered = false;
            }
            return currentLine[currentLineReadPos--];
        }
        return 0;
    }

    private void fillBuffer() throws IOException {
        if (currentFilePos < 0) {
            return;
        }

        if (currentFilePos < bufferSize) {
            in.seek(0);
            buffer = new byte[(int) currentFilePos + 1];
            in.readFully(buffer);
            currentBufferPos = (int) currentFilePos;
            currentFilePos = -1;
        } else {
            in.seek(currentFilePos - buffer.length);
            in.readFully(buffer);
            currentBufferPos = bufferSize - 1;
            currentFilePos = currentFilePos - bufferSize;
        }
    }

    private void fillLineBuffer() throws IOException {
        currentLineWritePos = 1;
        while (true) {

            // we've read all the buffer - need to fill it again
            if (currentBufferPos < 0) {
                fillBuffer();

                // nothing was buffered - we reached the beginning of a file
                if (currentBufferPos < 0) {
                    currentLineReadPos = currentLineWritePos - 1;
                    lineBuffered = true;
                    return;
                }
            }

            byte b = buffer[currentBufferPos--];

            // \n is found - line fully buffered
            if (b == 0xA) {
                currentLineReadPos = currentLineWritePos - 1;
                lineBuffered = true;
                break;

                // just ignore \r for now
            } else if (b == 0xD) {
                continue;
            } else {
                if (currentLineWritePos == maxLineBytes) {
                    throw new IOException("file has a line exceeding " + maxLineBytes
                            + " bytes; use constructor to pickup bigger line buffer");
                }

                // write the current line bytes in reverse order - reading from
                // the end will produce the correct line
                currentLine[currentLineWritePos++] = b;
            }
        }
    }

}