2008-08-06 13 views
81

Tengo una aplicación que escribe información en un archivo. Esta información se usa después de la ejecución para determinar la aprobación/falla/corrección de la aplicación. Me gustaría poder leer el archivo tal como está siendo escrito para poder hacer estas comprobaciones de aprobación/falla/corrección en tiempo real.¿Cómo uso Java para leer desde un archivo en el que se está escribiendo activamente?

Supongo que es posible hacer esto, pero ¿qué implica el uso de Java al usar Java? Si la lectura alcanza la escritura, ¿solo esperará a que se escriban más hasta que se cierre el archivo, o la lectura arrojará una excepción en este momento? Si esto último, ¿qué hago entonces?

Mi intuición actualmente me está empujando hacia BufferedStreams. ¿Es este el camino a seguir?

+1

bueno, ya que estoy frente a un escenario similar que estaba redacción si se ha encontrado una solución mejor que la aceptada? –

+0

Sé que esta es una vieja pregunta, pero por el bien de los lectores en el futuro, ¿puede ampliar un poco más su caso de uso? Sin tener más información, uno se pregunta si tal vez está resolviendo el problema equivocado. – user359996

+2

Usa una base de datos. Estos escenarios de "leer un archivo mientras se está escribiendo" terminan en lágrimas. – EJP

Respuesta

34

No se pudo obtener el ejemplo de trabajar usando FileChannel.read(ByteBuffer) porque no es una lectura de bloqueo. Hizo sin embargo obtener el código siguiente para el trabajo:

boolean running = true; 
BufferedInputStream reader = new BufferedInputStream(new FileInputStream("out.txt")); 

public void run() { 
    while(running) { 
     if(reader.available() > 0) { 
      System.out.print((char)reader.read()); 
     } 
     else { 
      try { 
       sleep(500); 
      } 
      catch(InterruptedException ex) { 
       running = false; 
      } 
     } 
    } 
} 

Por supuesto, lo mismo que funcionaría como un temporizador en lugar de un hilo, pero eso se lo dejo para el programador. Todavía estoy buscando una mejor manera, pero esto funciona para mí por ahora.

Ah, y voy a advertir esto con: Estoy usando 1.4.2. Sí, sé que todavía estoy en las edades de piedra.

+1

Gracias por agregar esto ... algo que nunca hice. Creo que la respuesta de Blade de bloquear el archivo también es buena. Sin embargo, requiere Java 6 (creo). –

+0

@AnthonyCramp ¿por qué no estás dando la recompensa? :) –

+0

@JosephGordon - Tendrá que mudarse al Drone envejece uno de estos días ;-) – TungstenX

1

No es Java per se, pero puede encontrarse con problemas en los que ha escrito algo en un archivo, pero todavía no se ha escrito: puede estar en un caché en alguna parte y leer desde el mismo archivo puede en realidad, no te da la nueva información.

Versión corta: use flush() o cualquiera que sea la llamada del sistema relevante para asegurarse de que sus datos estén realmente escritos en el archivo.

Nota No estoy hablando de la memoria caché de disco del nivel del sistema operativo: si sus datos entran aquí, debería aparecer en una lectura() después de este punto. Es posible que el idioma en sí almacena en caché las escrituras, esperando a que se llene el búfer o que el archivo se vacíe/cierre.

0

Nunca lo he intentado, pero debe escribir un caso de prueba para ver si leer de una secuencia después de que haya tocado el final funcionará, independientemente de si hay más datos escritos en el archivo.

¿Hay algún motivo por el que no pueda usar un flujo de entrada/salida por tubería? ¿Los datos están siendo escritos y leídos desde la misma aplicación (si es así, usted tiene los datos, por qué necesita leer el archivo)?

De lo contrario, tal vez lea hasta el final del archivo, luego monitoree los cambios y busque dónde lo dejó y continúe ... aunque tenga cuidado con las condiciones de carrera.

3

La respuesta parece ser "no" ... y "sí". Parece que no hay forma real de saber si un archivo está abierto para escritura por otra aplicación. Por lo tanto, la lectura de dicho archivo solo progresará hasta que se agote el contenido. Tomé el consejo de Mike y escribí un código de prueba:

Writer.java escribe una cadena en un archivo y luego espera a que el usuario presione enter antes de escribir otra línea en el archivo. La idea es que se pueda iniciar, luego se puede comenzar a leer un lector para ver cómo funciona el archivo "parcial". El lector que escribí está en Reader.java.

Writer.java

public class Writer extends Object 
{ 
    Writer() { 

    } 

    public static String[] strings = 
     { 
      "Hello World", 
      "Goodbye World" 
     }; 

    public static void main(String[] args) 
     throws java.io.IOException { 

     java.io.PrintWriter pw = 
      new java.io.PrintWriter(new java.io.FileOutputStream("out.txt"), true); 

     for(String s : strings) { 
      pw.println(s); 
      System.in.read(); 
     } 

     pw.close(); 
    } 
} 

Reader.java

public class Reader extends Object 
{ 
    Reader() { 

    } 

    public static void main(String[] args) 
     throws Exception { 

     java.io.FileInputStream in = new java.io.FileInputStream("out.txt"); 

     java.nio.channels.FileChannel fc = in.getChannel(); 
     java.nio.ByteBuffer bb = java.nio.ByteBuffer.allocate(10); 

     while(fc.read(bb) >= 0) { 
      bb.flip(); 
      while(bb.hasRemaining()) { 
       System.out.println((char)bb.get()); 
      } 
      bb.clear(); 
     } 

     System.exit(0); 
    } 
} 

No hay garantías de que este código es la mejor práctica.

Esto deja la opción sugerida por Mike de verificar periódicamente si hay nuevos datos para leer del archivo. Esto requiere la intervención del usuario para cerrar el lector de archivos cuando se determina que la lectura se ha completado. O bien, el lector debe conocer el contenido del archivo y ser capaz de determinar y finalizar la condición de escritura. Si el contenido fuera XML, el final del documento podría usarse para señalar esto.

16

Analice el uso del Tailer de Apache Commons IO. Maneja la mayoría de los casos extremos.

1

Hay una cola de código abierto Java Graphic que hace esto.

https://stackoverflow.com/a/559146/1255493

public void run() { 
    try { 
     while (_running) { 
      Thread.sleep(_updateInterval); 
      long len = _file.length(); 
      if (len < _filePointer) { 
       // Log must have been jibbled or deleted. 
       this.appendMessage("Log file was reset. Restarting logging from start of file."); 
       _filePointer = len; 
      } 
      else if (len > _filePointer) { 
       // File must have had something added to it! 
       RandomAccessFile raf = new RandomAccessFile(_file, "r"); 
       raf.seek(_filePointer); 
       String line = null; 
       while ((line = raf.readLine()) != null) { 
        this.appendLine(line); 
       } 
       _filePointer = raf.getFilePointer(); 
       raf.close(); 
      } 
     } 
    } 
    catch (Exception e) { 
     this.appendMessage("Fatal error reading log file, log tailing has stopped."); 
    } 
    // dispose(); 
} 
5

estoy totalmente de acuerdo con Joshua's response, Tailer es adecuado para el trabajo en esta situación. Aquí está un ejemplo:

Se escribe una línea cada 150 ms en un archivo, mientras que la lectura de este mismo archivo de cada 2500 ms

public class TailerTest 
{ 
    public static void main(String[] args) 
    { 
     File f = new File("/tmp/test.txt"); 
     MyListener listener = new MyListener(); 
     Tailer.create(f, listener, 2500); 

     try 
     { 
      FileOutputStream fos = new FileOutputStream(f); 
      int i = 0; 
      while (i < 200) 
      { 
       fos.write(("test" + ++i + "\n").getBytes()); 
       Thread.sleep(150); 
      } 
      fos.close(); 
     } 
     catch (Exception e) 
     { 
      e.printStackTrace(); 
     } 
    } 

    private static class MyListener extends TailerListenerAdapter 
    { 
     @Override 
     public void handle(String line) 
     { 
      System.out.println(line); 
     } 
    } 
} 
+0

Su enlace a Tailer está roto. –

+1

@StealthRabbi Lo mejor que puedes hacer entonces es buscar el enlace correcto y editar la respuesta con él. – igracia

4

Si desea leer un archivo mientras se está escribiendo y sólo lea el nuevo contenido y luego lo ayudará a lograr lo mismo.

Para ejecutar este programa lo ejecutará desde el símbolo del sistema/ventana de terminal y pasará el nombre del archivo para leer. Leerá el archivo a menos que elimines el programa.

java FileReader c: \ miarchivo.txt

A medida que escribe una línea de texto salvarlo de bloc de notas y verá el texto impreso en la consola.

public class FileReader { 

    public static void main(String args[]) throws Exception { 
     if(args.length>0){ 
      File file = new File(args[0]); 
      System.out.println(file.getAbsolutePath()); 
      if(file.exists() && file.canRead()){ 
       long fileLength = file.length(); 
       readFile(file,0L); 
       while(true){ 

        if(fileLength<file.length()){ 
         readFile(file,fileLength); 
         fileLength=file.length(); 
        } 
       } 
      } 
     }else{ 
      System.out.println("no file to read"); 
     } 
    } 

    public static void readFile(File file,Long fileLength) throws IOException { 
     String line = null; 

     BufferedReader in = new BufferedReader(new java.io.FileReader(file)); 
     in.skip(fileLength); 
     while((line = in.readLine()) != null) 
     { 
      System.out.println(line); 
     } 
     in.close(); 
    } 
} 
+2

¿No existe la posibilidad de que, entre el momento en que se lea el archivo y el tiempo de duración del archivo, el proceso externo pueda agregar más datos al archivo? Si es así, esto daría como resultado que los datos faltantes del proceso de lectura se escriban en el archivo. – JohnC

+0

Este código consume mucha CPU porque el ciclo no tiene una llamada thread.sleep. Sin agregar una pequeña cantidad de retraso, este código tiende a mantener a la CPU muy ocupada. – ChaitanyaBhatt

0

No se puede leer un archivo que se abre desde otro proceso que utiliza FileInputStream, FileReader o RandomAccessFile.

Pero el uso de FileChannel directamente funcionará:

private static byte[] readSharedFile(File file) throws IOException { 
    byte buffer[] = new byte[(int) file.length()]; 
    final FileChannel fc = FileChannel.open(file.toPath(), EnumSet.of(StandardOpenOption.READ)); 
    final ByteBuffer dst = ByteBuffer.wrap(buffer); 
    fc.read(dst); 
    fc.close(); 
    return buffer; 
} 
Cuestiones relacionadas