2010-05-20 18 views
11

Sto lavorando a un prodotto software con un visualizzatore di file di registro integrato. Il problema è, è lento e instabile per i file molto grandi perché legge l'intero file in memoria quando si visualizza un file di registro. Sto volendo scrivere un nuovo visualizzatore di file di registro che risolva questo problema.Come scrivere un visualizzatore di file di testo Java per file di registro grandi

Quali sono le migliori pratiche per la scrittura di visualizzatori per file di testo di grandi dimensioni? In che modo gli editori come notepad ++ e VIM realizzano questo? Stavo pensando di usare un lettore di flusso di testo bidirezionale bufferizzato insieme a TableModel di Java. Sto pensando lungo le linee giuste e queste implementazioni sono disponibili per Java?

Modifica: conviene rivedere il file una volta per indicizzare le posizioni dell'inizio di ogni riga di testo in modo che si sappia dove cercare? Probabilmente avrò bisogno della quantità di linee, quindi probabilmente dovrò analizzare il file almeno una volta?

Edit2: Ho aggiunto la mia implementazione a una risposta di seguito. Per favore, commentalo o modificalo per aiutarmi/noi ad arrivare a un'implementazione di best practice o altrimenti a fornirti il ​​tuo.

risposta

4

Non sono sicuro che NotePad ++ implementi effettivamente l'accesso casuale, ma penso che sia la strada da percorrere, soprattutto con un visualizzatore di file di registro, il che implica che sarà di sola lettura.

Poiché il visualizzatore di registri sarà di sola lettura, è possibile utilizzare un file di memoria "stream" con sola lettura random access. In Java, questo è il FileChannel.

Quindi basta saltare nel file in base alle esigenze e visualizzare sullo schermo solo una finestra di scorrimento dei dati.

Uno dei vantaggi di FileChannel è che i thread concorrenti possono avere il file aperto e la lettura non influisce sul puntatore del file corrente. Quindi, se si aggiunge il file di registro in un altro thread, non sarà interessato.

Un altro vantaggio è che è possibile chiamare il metodo di dimensione di FileChannel per ottenere le dimensioni del file in qualsiasi momento.

Il problema con la mappatura della memoria direttamente in un file di accesso casuale, che alcuni editor di testo consentono (come HxD e UltraEdit), è che qualsiasi modifica influisce direttamente sul file. Pertanto, le modifiche sono immediate (eccetto per la cache in scrittura), che in genere non sono richieste dagli utenti. In genere, gli utenti non desiderano che le modifiche vengano apportate finché non fanno clic su Salva. Tuttavia, poiché questo è solo un visualizzatore, non hai le stesse preoccupazioni.

+0

Grazie, ho visto anche RandomAccessFile oltre a FileChannel che può rivelarsi utile –

2

Un approccio tipico consiste nell'utilizzare un lettore di file ricercabile, eseguire un passaggio nel registro registrando un indice di offset di riga e quindi presentare solo una finestra su una parte del file come richiesto.

Questo riduce sia i dati necessari in un richiamo rapido e non carica un widget in cui il 99% dei suoi contenuti non sono attualmente visibili.

0

Inserisco la mia implementazione di test (dopo aver seguito i consigli di Marcus Adams e MSW) qui per vostra comodità e anche per ulteriori commenti e critiche. È abbastanza veloce.

Non mi sono preoccupato della sicurezza della codifica Unicode. Immagino che questa sarà la mia prossima domanda. Qualsiasi suggerimento su quello molto benvenuto.

class LogFileTableModel implements TableModel { 

    private final File f; 
    private final int lineCount; 
    private final String errMsg; 
    private final Long[] index; 
    private final ByteBuffer linebuf = ByteBuffer.allocate(1024); 
    private FileChannel chan; 

    public LogFileTableModel(String filename) { 
     f = new File(filename); 
     String m; 
     int l = 1; 
     Long[] idx = new Long[] {}; 
     try { 
      FileInputStream in = new FileInputStream(f); 
      chan = in.getChannel(); 
      m = null; 
      idx = buildLineIndex(); 
      l = idx.length; 
     } catch (IOException e) { 
      m = e.getMessage(); 
     } 
     errMsg = m; 
     lineCount = l; 
     index = idx; 
    } 

    private Long[] buildLineIndex() throws IOException { 
     List<Long> idx = new LinkedList<Long>(); 
     idx.add(0L); 

     ByteBuffer buf = ByteBuffer.allocate(8 * 1024); 
     long offset = 0; 
     while (chan.read(buf) != -1) { 
      int len = buf.position(); 
      buf.rewind();    
      int pos = 0; 
      byte[] bufA = buf.array(); 
      while (pos < len) { 
       byte c = bufA[pos++]; 
       if (c == '\n') 
        idx.add(offset + pos); 
      } 
      offset = chan.position(); 
     } 
     System.out.println("Done Building index"); 
     return idx.toArray(new Long[] {}); 
    } 

    @Override 
    public int getColumnCount() { 
     return 2; 
    } 

    @Override 
    public int getRowCount() { 
     return lineCount; 
    } 

    @Override 
    public String getColumnName(int columnIndex) { 
     switch (columnIndex) { 
     case 0: 
      return "#"; 
     case 1: 
      return "Name"; 
     } 
     return ""; 
    } 

    @Override 
    public Object getValueAt(int rowIndex, int columnIndex) { 
     switch (columnIndex) { 
      case 0:     
       return String.format("%3d", rowIndex); 
      case 1: 
       if (errMsg != null) 
        return errMsg; 
       try { 
        Long pos = index[rowIndex]; 
        chan.position(pos); 
        chan.read(linebuf); 
        linebuf.rewind(); 
        if (rowIndex == lineCount - 1) 
         return new String(linebuf.array()); 
        else  
         return new String(linebuf.array(), 0, (int)(long)(index[rowIndex+1]-pos)); 
       } catch (Exception e) { 
        return "Error: "+ e.getMessage(); 
       } 
     }    
     return "a"; 
    } 

    @Override 
    public Class<?> getColumnClass(int columnIndex) { 
     return String.class; 
    } 

    // ... other methods to make interface complete 


} 
+0

Hmmm, ok, sembra che la mia applicazione è UTF-8 di sicurezza a causa di UTF-8 intrinseca di auto-sincronizzazione-ness.Il controllo di '\ n' che è binario 00100000 è univoco in UTF-8. Tutti i byte che fanno parte di una sequenza multi-byte avranno almeno il bit 8 impostato. –

Problemi correlati