2013-03-25 7 views
11

Ich muss die letzten n Zeilen aus einer großen Datei lesen (sagen wir 2GB). Die Datei ist UTF-8-codiert.Liest RandomAccessFile in Java die gesamte Datei im Speicher?

Ich möchte den effizientesten Weg wissen, es zu tun. Lesen Sie über RandomAccessFile in Java, aber liest die seek() -Methode die gesamte Datei im Speicher. Es verwendet native Implementierung, so dass ich den Quellcode nicht verweisen konnte.

+0

Und nein, 'seek()' liest nichts * in den Speicher, geschweige denn die ganze Datei. Sie haben die volle Kontrolle. – NPE

+0

Ich lese diese Frage durch, aber ich würde gerne verstehen, wenn die Datei wenn UTF-8 codiert, wird dann die Verwendung von RandomAccessFile abgeraten? –

+1

Nicht einverstanden mit Duplikat. Dies konzentriert sich mehr auf RandomAccessFile, während der andere sich mehr auf die Anwendung bezieht und RAF nicht einmal erwähnt. –

Antwort

6

1) RandomAccessFile.seek legt nur die aktuelle Position des Dateizeigers fest, es werden keine Bytes in den Speicher gelesen.

2) Da Ihre Datei UTF-8-codiert ist, handelt es sich um eine Textdatei. Zum Lesen von Textdateien verwenden wir in der Regel BufferedReader, Java 7 hat sogar die convenience-Methode File.newBufferedReader hinzugefügt, um eine Instanz eines BufferedReaders zu erstellen, um Text aus einer Datei zu lesen. Obwohl es ineffizient sein kann, die letzten n Zeilen zu lesen, ist es jedoch einfach zu implementieren.

3) Um effizient zu sein, benötigen wir RandomAccessFile und lesen die Datei rückwärts beginnend vom Ende. Hier ist ein einfaches Beispiel

public static void main(String[] args) throws Exception { 
    int n = 3; 
    List<String> lines = new ArrayList<>(); 
    try (RandomAccessFile f = new RandomAccessFile("test", "r")) { 
     ByteArrayOutputStream bout = new ByteArrayOutputStream(); 
     for (long length = f.length(), p = length - 1; p > 0 && lines.size() < n; p--) { 
      f.seek(p); 
      int b = f.read(); 
      if (b == 10) { 
       if (p < length - 1) { 
        lines.add(0, getLine(bout)); 
        bout.reset(); 
       } 
      } else if (b != 13) { 
       bout.write(b); 
      } 
     } 
    } 
    System.out.println(lines); 
} 

static String getLine(ByteArrayOutputStream bout) { 
    byte[] a = bout.toByteArray(); 
    // reverse bytes 
    for (int i = 0, j = a.length - 1; j > i; i++, j--) { 
     byte tmp = a[j]; 
     a[j] = a[i]; 
     a[i] = tmp; 
    } 
    return new String(a); 
} 

Es liest die Datei Byte für Byte Ausgang vom Schwanz zum ByteArrayOutputStream, wenn LF erreicht wird, die Bytes umkehrt und erstellt eine Zeile.

Zwei Dinge müssen verbessert werden: 1) Pufferung 2) EOL Erkennung

+1

Können Sie angeben, wie BufferedReader verwendet wird, ohne die gesamte Datei zu lesen? –

+0

Da es zeilenweise liest, liest es nicht die ganze Datei in den Speicher –

+0

Ich würde sagen, da es zeilenweise von Anfang an liest, liest es die ganze Datei in den Speicher, auch wenn es nicht das ganze lädt Datei sofort. –

0

Wenn Sie Random Access benötigen, können Sie Random benötigen. Sie können die Bytes, die Sie daraus erhalten, in UTF-8 konvertieren, wenn Sie wissen, was Sie tun.

Wenn Sie BuffredReader verwenden, können Sie skip (n) nach Anzahl der Zeichen verwenden, was bedeutet, dass es die gesamte Datei lesen muss.


Ein Weg, dies in Kombination zu tun; Verwenden Sie FileInputStream mit skip(), suchen Sie, wo Sie lesen möchten, indem Sie N Zeilenumbrüche zurücklesen und dann den Stream in BufferedReader umhüllen, um die Zeilen mit UTF-8-Codierung zu lesen.

+0

Also heißt es, Ende des Tages, ich am Ende die ganze Datei zu lesen in Erinnerung ? –

+0

Nicht, wenn Sie tun, was ich vorschlage. Wenn Sie BufferedReader alleine verwenden, wird es die ganze Datei lesen, was ich Ihnen nicht vorschlage. –

+0

Können Sie bitte ein Code-Schnipsel für diesen Anfänger teilen :(. Ich möchte das Ende der Datei erreichen, zurück zu n Zeilen und dann lesen Sie die n Zeilen in meinem Speicher –