2009-03-26 25 views

Respuesta

18

Eche un vistazo a mi respuesta a un similar question for C#. El código sería bastante similar, aunque el soporte de codificación es algo diferente en Java.

Básicamente no es algo terriblemente fácil de hacer en general. Como señala MSalter, UTF-8 hace que sea fácil detectar \r o \n ya que la representación UTF-8 de esos caracteres es igual a ASCII, y esos bytes no ocurrirán en caracteres de múltiples bytes.

Así que, básicamente, tome un buffer de (digamos) 2K, y lea progresivamente hacia atrás (salte a 2K antes que antes, lea los próximos 2K) verificando la terminación de una línea. Luego omita exactamente el lugar correcto en la secuencia, cree un InputStreamReader en la parte superior y un BufferedReader encima de eso. Entonces simplemente llame al BufferedReader.readLine().

+2

UTF-8 no importa - lo que necesita la última CR o de salto, que es un solo byte en ASCII y UTF -8. – MSalters

+0

@MSalters: Buen punto. Se actualizará ... –

0

En C#, usted debe ser capaz de establecer la posición de la corriente:

Desde: http://bytes.com/groups/net-c/269090-streamreader-read-last-line-text-file

using(FileStream fs = File.OpenRead("c:\\file.dat")) 
{ 
    using(StreamReader sr = new StreamReader(fs)) 
    { 
     sr.BaseStream.Position = fs.Length - 4; 
     if(sr.ReadToEnd() == "DONE") 
      // match 
    } 
} 
+0

En FileInputStream de Java (en el que se basa FileReader), no puede establecer la posición; solo puede saltar hacia adelante, lo que probablemente no lea las partes que salteó, pero sigue siendo una operación unidireccional y por lo tanto no adecuada para buscar un salto de línea en un desplazamiento desconocido desde el final. –

+0

Bueno ... Intenté: P – rball

+0

Puede usar mark() para evitar ese problema, dependiendo de lo que sea la corriente markLimit(). –

3

Usando FileReader o FileInputStream no funcionará - Vas a tener que utilizar FileChannel o RandomAccessFile para recorrer el archivo hacia atrás desde el final. Las codificaciones serán un problema, como dijo Jon.

+0

Tenga en cuenta que el rendimiento de RandomAccessFile es una mierda para operaciones individuales, por lo que las lecturas de tamaño razonables se realizan en un búfer. –

79

A continuación se muestran dos funciones, una que devuelve la última línea no en blanco de un archivo sin cargar o recorrer el archivo completo, y la otra que devuelve las últimas N líneas del archivo sin recorrer todo el archivo:

Lo que hace la cola es acercar directamente al último carácter del archivo, luego retrocede, carácter por carácter, grabando lo que ve hasta que encuentra un salto de línea. Una vez que encuentra un salto de línea, se rompe el ciclo. Invierte lo que se grabó y lo arroja a una cadena y regresa. 0xA es la nueva línea y 0xD es el retorno de carro.

Si sus terminaciones de línea son \r\n o crlf o alguna otra "nueva línea de estilo de nueva línea doble", tendrá que especificar n * 2 líneas para obtener las últimas n líneas porque cuenta 2 líneas para cada línea.

public String tail(File file) { 
    RandomAccessFile fileHandler = null; 
    try { 
     fileHandler = new RandomAccessFile(file, "r"); 
     long fileLength = fileHandler.length() - 1; 
     StringBuilder sb = new StringBuilder(); 

     for(long filePointer = fileLength; filePointer != -1; filePointer--){ 
      fileHandler.seek(filePointer); 
      int readByte = fileHandler.readByte(); 

      if(readByte == 0xA) { 
       if(filePointer == fileLength) { 
        continue; 
       } 
       break; 

      } else if(readByte == 0xD) { 
       if(filePointer == fileLength - 1) { 
        continue; 
       } 
       break; 
      } 

      sb.append((char) readByte); 
     } 

     String lastLine = sb.reverse().toString(); 
     return lastLine; 
    } catch(java.io.FileNotFoundException e) { 
     e.printStackTrace(); 
     return null; 
    } catch(java.io.IOException e) { 
     e.printStackTrace(); 
     return null; 
    } finally { 
     if (fileHandler != null) 
      try { 
       fileHandler.close(); 
      } catch (IOException e) { 
       /* ignore */ 
      } 
    } 
} 

Pero es probable que no desea que la última línea, que quieren que los últimos N líneas, a fin de utilizar esto en su lugar:

public String tail2(File file, int lines) { 
    java.io.RandomAccessFile fileHandler = null; 
    try { 
     fileHandler = 
      new java.io.RandomAccessFile(file, "r"); 
     long fileLength = fileHandler.length() - 1; 
     StringBuilder sb = new StringBuilder(); 
     int line = 0; 

     for(long filePointer = fileLength; filePointer != -1; filePointer--){ 
      fileHandler.seek(filePointer); 
      int readByte = fileHandler.readByte(); 

      if(readByte == 0xA) { 
       if (filePointer < fileLength) { 
        line = line + 1; 
       } 
      } else if(readByte == 0xD) { 
       if (filePointer < fileLength-1) { 
        line = line + 1; 
       } 
      } 
      if (line >= lines) { 
       break; 
      } 
      sb.append((char) readByte); 
     } 

     String lastLine = sb.reverse().toString(); 
     return lastLine; 
    } catch(java.io.FileNotFoundException e) { 
     e.printStackTrace(); 
     return null; 
    } catch(java.io.IOException e) { 
     e.printStackTrace(); 
     return null; 
    } 
    finally { 
     if (fileHandler != null) 
      try { 
       fileHandler.close(); 
      } catch (IOException e) { 
      } 
    } 
} 

invocar a los métodos anteriores de esta manera:

File file = new File("D:\\stuff\\huge.log"); 
System.out.println(tail(file)); 
System.out.println(tail2(file, 10)); 

Advertencia En la naturaleza al oeste de Unicode este código puede causar que la salida de esta función salga mal. Por ejemplo, "María" en lugar de "María". Los caracteres con hats, accents, Chinese characters etc. pueden hacer que la salida sea incorrecta porque los acentos se agregan como modificadores después del carácter. La inversión de caracteres compuestos cambia la naturaleza de la identidad del personaje en la reversión. Tendrá que hacer una batería completa de pruebas en todos los idiomas con los que planea usar esto.

Para obtener más información acerca de este problema reversión Unicode leyeron: http://msmvps.com/blogs/jon_skeet/archive/2009/11/02/omg-ponies-aka-humanity-epic-fail.aspx

+2

Lo anterior no tiene en cuenta las líneas terminadas con CR y LF. – Jags

+1

su implementación multilínea no funciona en sus casos especiales de filePointer == fileLength, la línea se mantendrá igual, por lo tanto, la línea de condición == no se activará después de eso y el código leerá todo el archivo. – ZPiDER

0

Usted puede cambiar fácilmente el código de abajo para imprimir la última línea.

archivo proyectado en memoria para la impresión últimos 5 líneas:

private static void printByMemoryMappedFile(File file) throws FileNotFoundException, IOException{ 
     FileInputStream fileInputStream=new FileInputStream(file); 
     FileChannel channel=fileInputStream.getChannel(); 
     ByteBuffer buffer=channel.map(FileChannel.MapMode.READ_ONLY, 0, channel.size()); 
     buffer.position((int)channel.size()); 
     int count=0; 
     StringBuilder builder=new StringBuilder(); 
     for(long i=channel.size()-1;i>=0;i--){ 
      char c=(char)buffer.get((int)i); 
      builder.append(c); 
      if(c=='\n'){ 
       if(count==5)break; 
       count++; 
       builder.reverse(); 
       System.out.println(builder.toString()); 
       builder=null; 
       builder=new StringBuilder(); 
      } 
     } 
     channel.close(); 
    } 

RandomAccessFile para imprimir últimos 5 líneas:

private static void printByRandomAcessFile(File file) throws FileNotFoundException, IOException{ 
     RandomAccessFile randomAccessFile = new RandomAccessFile(file, "r"); 
     int lines = 0; 
     StringBuilder builder = new StringBuilder(); 
     long length = file.length(); 
     length--; 
     randomAccessFile.seek(length); 
     for(long seek = length; seek >= 0; --seek){ 
      randomAccessFile.seek(seek); 
      char c = (char)randomAccessFile.read(); 
      builder.append(c); 
      if(c == '\n'){ 
       builder = builder.reverse(); 
       System.out.println(builder.toString()); 
       lines++; 
       builder = null; 
       builder = new StringBuilder(); 
       if (lines == 5){ 
        break; 
       } 
      } 

     } 
    } 
25

Apache Commons tiene una implementación usando RandomAccessFile.

Se llama ReversedLinesFileReader.

+0

Creo que esta es la forma más rápida de leer el archivo en orden inverso –

+0

parece que no lee n líneas como la respuesta aceptada. – JuanToroMarty

+1

@JuanToroMarty Es posible recorrer el método 'readLine()'. – Stephan

0

Ésta es una manera de que me hizo pasar :) Espero que ayude

try(BufferedReader reader = new BufferedReader(new FileReader(reqFile))){ 

     String line = null; 


     System.out.println("======================================"); 

     line = reader.readLine();  //Read Line ONE 
     line = reader.readLine();  //Read Line TWO 
     System.out.println("first line : " + line); 

     //Length of one line if lines are of even length 
     int len = line.length();  

     //skip to the end - 3 lines 
     reader.skip((reqFile.length() - (len*3))); 

     //Searched to the last line for the date I was looking for. 

     while((line = reader.readLine()) != null){ 

      System.out.println("FROM LINE : " + line); 
      String date = line.substring(0,line.indexOf(",")); 

      System.out.println("DATE : " + date);  //BAM!!!!!!!!!!!!!! 


     } 

     System.out.println(reqFile.getName() + " Read(" + reqFile.length()/(1000) + "KB)"); 
     System.out.println("======================================"); 
    } catch (IOException x){ 
     x.printStackTrace(); 
    } 
Cuestiones relacionadas