2013-03-25 10 views
11

Ho bisogno di leggere le ultime n righe da un file di grandi dimensioni (ad esempio 2 GB). Il file è codificato in UTF-8.RandomAccessFile in java legge l'intero file in memoria?

Vorrei conoscere il modo più efficiente per farlo. Leggi di RandomAccessFile in java, ma fa il metodo seek(), leggi l'intero file in memoria. Usa un'implementazione nativa quindi non ero in grado di riferire il codice sorgente.

+0

E no, 'seek()' non legge * nulla * in memoria, per non parlare dell'intero file. Hai il pieno controllo. – NPE

+0

Ho letto quella domanda, ma mi piacerebbe capire, se il file è codificato in UTF-8, quindi l'uso di RandomAccessFile è scoraggiato? –

+1

Non sono d'accordo con il duplicato. Questo si concentra maggiormente su RandomAccessFile, mentre l'altro riguarda maggiormente l'applicazione e non menziona nemmeno RAF. –

risposta

6

1) RandomAccessFile.seek imposta solo la posizione corrente del puntatore del file, nessun byte viene letto nella memoria.

2) Poiché il file è codificato in UTF-8, è un file di testo. Per la lettura dei file di testo in genere si utilizza BufferedReader, Java 7 ha anche aggiunto un metodo di convinzioni File.newBufferedReader per creare un'istanza di BufferedReader per leggere il testo da un file. Sebbene possa essere inefficiente per la lettura delle ultime n righe, ma facile da implementare.

3) Per essere efficienti abbiamo bisogno di RandomAccessFile e di leggere i file a ritroso a partire dalla fine. Ecco un esempio di base

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

E 'legge il file di byte dopo byte iniziale dalla coda alla ByteArrayOutputStream, quando LF viene raggiunta inverte i byte e crea una linea.

Due cose devono essere migliorate: 1) il buffering riconoscimento 2) EOL

+1

Puoi includere come usare BufferedReader senza leggere l'intero file? –

+0

Poiché legge riga per riga non sta leggendo l'intero file nella memoria –

+0

Direi che poiché sta leggendo riga per riga dall'inizio, sta leggendo l'intero file in memoria, anche se non carica l'intero file in una volta. –

0

Se avete bisogno di accesso casuale, è necessario RandomAccessFile. Puoi convertire i byte che ottieni da questo in UTF-8 se sai cosa stai facendo.

Se si utilizza BuffredReader, è possibile utilizzare skip (n) per numero di caratteri, il che significa che deve leggere l'intero file.


Un modo per farlo in combinazione; è quello di utilizzare FileInputStream con skip(), trovare dove si desidera leggere leggendo indietro N newlines e quindi avvolgere il flusso in BufferedReader per leggere le righe con la codifica UTF-8.

+0

Quindi vuol dire che, alla fine della giornata, finisco per leggere l'intero file in memoria ? –

+0

Non se lo fai come suggerisco. Se usi BufferedReader da solo, leggerà l'intero file, che è quello che non ti suggerisco di fare. –

+0

Puoi per favore condividere uno snippet di codice per questo novizio :(. Voglio raggiungere la fine del file, risalire a n linee, e quindi leggere le n righe nella mia memoria –