Java: lee las últimas n líneas de un archivo ENORME

Quiero leer las últimas n líneas de un archivo muy grande sin leer todo el archivo en ningún área de memoria intermedia / memoria usando Java.

Miré alrededor de las API de JDK y Apache Commons I / O y no puedo encontrar una que sea adecuada para este propósito.

Estaba pensando en la forma en que cola o menos lo hace en UNIX. No creo que carguen todo el archivo y luego muestran las últimas líneas del archivo. Debería haber una forma similar de hacer lo mismo en Java también.

Si usa un RandomAccessFile , puede usar la length y seek llegar a un punto específico cerca del final del archivo y luego leer desde allí.

Si encuentra que no hay suficientes líneas, retroceda desde ese punto y vuelva a intentarlo. Una vez que haya descubierto dónde comienza la última línea, puede buscar allí y solo leer e imprimir.

Se puede hacer una suposición inicial de mejor estimación basada en sus propiedades de datos. Por ejemplo, si se trata de un archivo de texto, es posible que las longitudes de línea no excedan un promedio de 132, entonces, para obtener las últimas cinco líneas, comience 660 caracteres antes del final. Luego, si te equivocaste, vuelve a intentarlo en 1320 (incluso puedes usar lo que aprendiste de los últimos 660 caracteres para ajustarlo, por ejemplo: si esos 660 caracteres son solo tres líneas, el siguiente bash podría ser 660/3 * 5, más tal vez un poco más por si acaso).

Lo encontré de la manera más simple de hacer usando ReversedLinesFileReader desde apache commons-io api. Este método le dará la línea de abajo a arriba de un archivo y puede especificar n_lines value para especificar el número de línea.

 import org.apache.commons.io.input.ReversedLinesFileReader; File file = new File("D:\\file_name.xml"); int n_lines = 10; int counter = 0; ReversedLinesFileReader object = new ReversedLinesFileReader(file); while(counter < n_lines) { System.out.println(object.readLine()); counter++; } 

RandomAccessFile es un buen lugar para comenzar, como se describe en las otras respuestas. Sin embargo, hay una advertencia importante .

Si su archivo no está codificado con una encoding de un byte por carácter, el método readLine() no funcionará para usted. Y readUTF() no funcionará en ninguna circunstancia. (Lee una cadena precedida de un recuento de caracteres …)

En su lugar, deberá asegurarse de buscar marcadores de final de línea que respeten los límites de caracteres de la encoding. Para codificaciones de longitud fija (por ejemplo, los sabores de UTF-16 o UTF-32) necesita extraer caracteres a partir de posiciones de bytes que son divisibles por el tamaño del carácter en bytes. Para codificaciones de longitud variable (por ejemplo, UTF-8), debe buscar un byte que debe ser el primer byte de un carácter.

En el caso de UTF-8, el primer byte de un carácter será 0xxxxxxx o 110xxxxx o 1110xxxx o 11110xxx . Cualquier otra cosa es un segundo / tercer byte o una secuencia UTF-8 ilegal. Consulte El estándar Unicode, Versión 5.2, Capítulo 3.9 , Tabla 3-7. Esto significa, como lo señala la discusión de comentarios, que cualquier byte 0x0A y 0x0D en una secuencia UTF-8 codificada adecuadamente representará un carácter LF o CR. Por lo tanto, contar los bytes es una estrategia de implementación válida (para UTF-8).

Habiendo identificado un límite de caracteres adecuado, puede llamar a una new String(...) pasando la matriz de bytes, desplazamiento, recuento y encoding, y luego repetidamente llamar a String.lastIndexOf(...) para contar el final de las líneas.

Encontré RandomAccessFile y otras clases de Buffer Reader demasiado lentas para mí. Nada puede ser más rápido que una tail -<#lines> . Esta fue la mejor solución para mí.

 public String getLastNLogLines(File file, int nLines) { StringBuilder s = new StringBuilder(); try { Process p = Runtime.getRuntime().exec("tail -"+nLines+" "+file); java.io.BufferedReader input = new java.io.BufferedReader(new java.io.InputStreamReader(p.getInputStream())); String line = null; //Here we first read the next line into the variable //line and then check for the EOF condition, which //is the return value of null while((line = input.readLine()) != null){ s.append(line+'\n'); } } catch (java.io.IOException e) { e.printStackTrace(); } return s.toString(); } 

CircularFifoBuffer de apache commons. respuesta de una pregunta similar en Cómo leer las últimas 5 líneas de un archivo .txt en Java

Tenga en cuenta que en Apache Commons Collections 4 esta clase parece haber sido renombrada a CircularFifoQueue

Un RandomAccessFile permite buscar (http://download.oracle.com/javase/1.4.2/docs/api/java/io/RandomAccessFile.html). El método File.length devolverá el tamaño del archivo. El problema es determinar el número de líneas. Para esto, puede buscar hasta el final del archivo y leer hacia atrás hasta que haya alcanzado el número correcto de líneas.

Tuve un problema similar, pero no entendí otras soluciones.

Usé esto. Espero que sea un código simple.

 // String filePathName = (direction and file name). File f = new File(filePathName); long fileLength = f.length(); // Take size of file [bites]. long fileLength_toRead = 0; if (fileLength > 2000) { // My file content is a table, I know one row has about eg 100 bites / characters. // I used 1000 bites before file end to point where start read. // If you don't know line length, use @paxdiablo advice. fileLength_toRead = fileLength - 1000; } try (RandomAccessFile raf = new RandomAccessFile(filePathName, "r")) { // This row manage open and close file. raf.seek(fileLength_toRead); // File will begin read at this bite. String rowInFile = raf.readLine(); // First readed line usualy is not whole, I needn't it. rowInFile = raf.readLine(); while (rowInFile != null) { // Here I can readed lines (rowInFile) add to String[] array or ArriyList. // Later I can work with rows from array - last row is sometimes empty, etc. rowInFile = raf.readLine(); } } catch (IOException e) { // } 

Esta es la mejor manera que he encontrado para hacerlo. Simple y bastante rápido y eficiente con la memoria.

 public static void tail(File src, OutputStream out, int maxLines) throws FileNotFoundException, IOException { BufferedReader reader = new BufferedReader(new FileReader(src)); String[] lines = new String[maxLines]; int lastNdx = 0; for (String line=reader.readLine(); line != null; line=reader.readLine()) { if (lastNdx == lines.length) { lastNdx = 0; } lines[lastNdx++] = line; } OutputStreamWriter writer = new OutputStreamWriter(out); for (int ndx=lastNdx; ndx != lastNdx-1; ndx++) { if (ndx == lines.length) { ndx = 0; } writer.write(lines[ndx]); writer.write("\n"); } writer.flush(); } 
  int n_lines = 1000; ReversedLinesFileReader object = new ReversedLinesFileReader(new File(path)); String result=""; for(int i=0;i