Número de líneas en un archivo en Java
Utilizo archivos de datos enormes, a veces solo necesito saber el número de líneas de estos archivos, normalmente los abro y los leo línea por línea hasta llegar al final del archivo.
Me preguntaba si hay una manera más inteligente de hacer eso.
Esta es la versión más rápida que he encontrado hasta ahora, aproximadamente 6 veces más rápida que readLines. En un archivo de registro de 150 MB, esto demora 0,35 segundos, frente a 2,40 segundos cuando se utiliza readLines(). Solo por diversión, el comando wc -l de Linux tarda 0,15 segundos.
public static int countLinesOld(String filename) throws IOException {
InputStream is = new BufferedInputStream(new FileInputStream(filename));
try {
byte[] c = new byte[1024];
int count = 0;
int readChars = 0;
boolean empty = true;
while ((readChars = is.read(c)) != -1) {
empty = false;
for (int i = 0; i < readChars; ++i) {
if (c[i] == '\n') {
++count;
}
}
}
return (count == 0 && !empty) ? 1 : count;
} finally {
is.close();
}
}
EDITAR, 9 años y medio después: prácticamente no tengo experiencia en Java, pero de todos modos he intentado comparar este código con la LineNumberReader
solución a continuación, ya que me molestaba que nadie lo hiciera. Parece que, especialmente para archivos grandes, mi solución es más rápida. Aunque parece que se necesitan algunas ejecuciones hasta que el optimizador haga un trabajo decente. Jugué un poco con el código y produje una nueva versión que es consistentemente más rápida:
public static int countLinesNew(String filename) throws IOException {
InputStream is = new BufferedInputStream(new FileInputStream(filename));
try {
byte[] c = new byte[1024];
int readChars = is.read(c);
if (readChars == -1) {
// bail out if nothing to read
return 0;
}
// make it easy for the optimizer to tune this loop
int count = 0;
while (readChars == 1024) {
for (int i=0; i<1024;) {
if (c[i++] == '\n') {
++count;
}
}
readChars = is.read(c);
}
// count remaining characters
while (readChars != -1) {
for (int i=0; i<readChars; ++i) {
if (c[i] == '\n') {
++count;
}
}
readChars = is.read(c);
}
return count == 0 ? 1 : count;
} finally {
is.close();
}
}
Resultados de referencia para un archivo de texto de 1,3 GB, eje y en segundos. Realicé 100 ejecuciones con el mismo archivo y medí cada ejecución con System.nanoTime()
. Puede ver que countLinesOld
tiene algunos valores atípicos y countLinesNew
ninguno y, aunque es solo un poco más rápido, la diferencia es estadísticamente significativa. LineNumberReader
es claramente más lento.
Implementé otra solución al problema, la encontré más eficiente al contar filas:
try
(
FileReader input = new FileReader("input.txt");
LineNumberReader count = new LineNumberReader(input);
)
{
while (count.skip(Long.MAX_VALUE) > 0)
{
// Loop just in case the file is > Long.MAX_VALUE or skip() decides to not read the entire file
}
result = count.getLineNumber() + 1; // +1 because line index starts at 0
}
La respuesta aceptada tiene un error de uno para archivos de varias líneas que no terminan en una nueva línea. Un archivo de una línea que termine sin una nueva línea devolverá 1, pero un archivo de dos líneas que termine sin una nueva línea también devolverá 1. Aquí hay una implementación de la solución aceptada que soluciona este problema. Las comprobaciones de EndsWithoutNewLine son un desperdicio para todo excepto para la lectura final, pero deberían ser triviales en términos de tiempo en comparación con la función general.
public int count(String filename) throws IOException {
InputStream is = new BufferedInputStream(new FileInputStream(filename));
try {
byte[] c = new byte[1024];
int count = 0;
int readChars = 0;
boolean endsWithoutNewLine = false;
while ((readChars = is.read(c)) != -1) {
for (int i = 0; i < readChars; ++i) {
if (c[i] == '\n')
++count;
}
endsWithoutNewLine = (c[readChars - 1] != '\n');
}
if(endsWithoutNewLine) {
++count;
}
return count;
} finally {
is.close();
}
}