2010-05-20 15 views
11

Estoy trabajando en un producto de software con un visor de archivos de registro integrado. El problema es que es lento e inestable para archivos realmente grandes porque lee el archivo completo en la memoria cuando se ve un archivo de registro. Quiero escribir un nuevo visor de archivos de registro que resuelva este problema.¿Cómo escribo un visualizador de archivos de texto Java para grandes archivos de registro?

¿Cuáles son las mejores prácticas para escribir visores para archivos de texto grandes? ¿Cómo los editores como notepad ++ y VIM logran esto? Estaba pensando en usar un lector de secuencia de texto bidireccional con buffer junto con TableModel de Java. ¿Estoy pensando en las líneas correctas y esas implementaciones de flujo están disponibles para Java?

Editar: ¿Valdrá la pena ejecutar el archivo una vez para indexar las posiciones del inicio de cada línea de texto para que sepa dónde buscar? Probablemente necesite la cantidad de líneas, ¿entonces probablemente tendré que escanear el archivo al menos una vez?

Edit2: He agregado mi implementación a la siguiente respuesta. Coméntelo o edítelo para ayudarme/a llegar a una implementación de mejores prácticas o proporcione la suya propia.

Respuesta

4

No estoy seguro de que NotePad ++ realmente implemente el acceso aleatorio, pero creo que ese es el camino a seguir, especialmente con un visor de archivos de registro, lo que implica que será de solo lectura.

Dado que su visor de registro será de solo lectura, puede usar un archivo mapeado de memoria random access de solo lectura "secuencia". En Java, este es el FileChannel.

A continuación, simplemente saltee el archivo según sea necesario y visualice en la pantalla solo una ventana de desplazamiento de los datos.

Una de las ventajas de FileChannel es que los subprocesos simultáneos pueden tener el archivo abierto, y la lectura no afecta al puntero del archivo actual. Por lo tanto, si agrega al archivo de registro en otro hilo, no se verá afectado.

Otra ventaja es que puede llamar al método de tamaño de FileChannel para obtener el tamaño del archivo en cualquier momento.

El problema con la memoria de mapeo directamente a un archivo de acceso aleatorio, que algunos editores de texto permiten (como HxD y UltraEdit), es que cualquier cambio afecta directamente al archivo. Por lo tanto, los cambios son inmediatos (excepto para el almacenamiento en caché de escritura), que es algo que los usuarios normalmente no desean. En cambio, los usuarios generalmente no desean que se realicen los cambios hasta que hagan clic en Guardar. Sin embargo, dado que esto es solo un visor, no tiene las mismas preocupaciones.

+0

Gracias, yo también vio RandomAccessFile además de FileChannel que puede resultar útil –

2

Un enfoque típico es utilizar un lector de archivos buscables, realizar una pasada a través del registro registrando un índice de desplazamientos de línea y luego presentar solo una ventana en una parte del archivo según lo solicitado.

Esto reduce los datos que necesita en la recuperación rápida y no carga un widget donde el 99% de sus contenidos no están visibles actualmente.

0

Publiqué mi implementación de prueba (después de seguir los consejos de Marcus Adams y msw) aquí para su conveniencia y también para más comentarios y críticas. Es bastante rápido.

No me he molestado con la seguridad de codificación Unicode. Supongo que esta será mi próxima pregunta. Cualquier sugerencia sobre eso muy bienvenido.

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, bien, parece como si mi aplicación es UTF-8 seguro debido inherente auto-sincronización-ción de UTF-8.La comprobación de '\ n' que es binaria 00100000 es única en UTF-8. Todos los bytes que forman parte de una secuencia de múltiples bytes tendrán al menos el bit 8 establecido. –

Cuestiones relacionadas