[Php] Contando eficientemente el número de líneas de un archivo de texto. (200mb +)


Answers

Sin embargo, usar un bucle de llamadas de fgets() es una buena solución y la más sencilla de escribir:

  1. aunque internamente el archivo se lee utilizando un búfer de 8192 bytes, su código aún tiene que llamar a esa función para cada línea.

  2. es técnicamente posible que una sola línea sea más grande que la memoria disponible si está leyendo un archivo binario.

Este código lee un archivo en pedazos de 8kB cada uno y luego cuenta el número de nuevas líneas dentro de ese fragmento.

function getLines($file)
{
    $f = fopen($file, 'rb');
    $lines = 0;

    while (!feof($f)) {
        $lines += substr_count(fread($f, 8192), "\n");
    }

    fclose($f);

    return $lines;
}

Si la longitud promedio de cada línea es como máximo de 4kB, ya comenzará a guardar en llamadas a funciones, y esas pueden sumarse cuando procesa archivos grandes.

Punto de referencia

Ejecuté una prueba con un archivo de 1GB; aquí están los resultados:

             +-------------+------------------+---------+
             | This answer | Dominic's answer | wc -l   |
+------------+-------------+------------------+---------+
| Lines      | 3550388     | 3550389          | 3550388 |
+------------+-------------+------------------+---------+
| Runtime    | 1.055       | 4.297            | 0.587   |
+------------+-------------+------------------+---------+

El tiempo se mide en segundos en tiempo real, mira aquí lo que significa real

Question

Acabo de descubrir que mi script me da un error fatal:

Fatal error: Allowed memory size of 268435456 bytes exhausted (tried to allocate 440 bytes) in C:\process_txt.php on line 109

Esa línea es esta:

$lines = count(file($path)) - 1;

Entonces, creo que es difícil cargar el archivo en memeory y contar el número de líneas, ¿hay alguna manera más eficiente de hacerlo sin tener problemas de memoria?

Los archivos de texto que necesito para contar el número de líneas para el rango de 2MB a 500MB. Tal vez un concierto a veces.

Gracias a todos por cualquier ayuda.




Uso este método solo para contar cuántas líneas hay en un archivo. ¿Cuál es la desventaja de hacer esto en las otras respuestas? Estoy viendo muchas líneas en lugar de mi solución de dos líneas. Supongo que hay una razón por la que nadie hace esto.

$lines = count(file('your.file'));
echo $lines;



Hay otra respuesta que pensé que podría ser una buena adición a esta lista.

Si tiene perl instalado y puede ejecutar cosas desde el shell en PHP:

$lines = exec('perl -pe \'s/\r\n|\n|\r/\n/g\' ' . escapeshellarg('largetextfile.txt') . ' | wc -l');

Esto debería manejar la mayoría de los saltos de línea ya sea desde Unix o archivos creados por Windows.

DOS desventajas (al menos):

1) No es una gran idea tener tu script tan dependiente del sistema en el que se ejecuta (puede no ser seguro asumir que Perl y wc están disponibles)

2) Solo un pequeño error al escapar y ha entregado acceso a un caparazón en su máquina.

Como con la mayoría de las cosas que sé (o creo que sé) sobre la codificación, obtuve esta información de otro lado:

Artículo de John Reeve




Solución de Objeto Orientado Simple

$file = new \SplFileObject('file.extension');

while($file->valid()) $file->fgets();

var_dump($file->key());

Actualizar

Otra forma de hacer esto es con PHP_INT_MAX en el método SplFileObject::seek .

$file = new \SplFileObject('file.extension', 'r');
$file->seek(PHP_INT_MAX);

echo $file->key() + 1; 



Tienes varias opciones. El primero es aumentar la memoria disponible permitida, que probablemente no sea la mejor manera de hacer las cosas, ya que el archivo puede ser muy grande. La otra forma es usar fgets para leer el archivo línea por línea e incrementar un contador, lo que no debería causar ningún problema de memoria ya que solo la línea actual está en la memoria en un momento dado.




Si está utilizando PHP 5.5, puede usar un generador . Esto NO funcionará en ninguna versión de PHP anterior a 5.5. Desde php.net:

"Los generadores proporcionan una forma fácil de implementar iteradores simples sin la sobrecarga o la complejidad de implementar una clase que implemente la interfaz Iterator".

// This function implements a generator to load individual lines of a large file
function getLines($file) {
    $f = fopen($file, 'r');

    // read each line of the file without loading the whole file to memory
    while ($line = fgets($f)) {
        yield $line;
    }
}

// Since generators implement simple iterators, I can quickly count the number
// of lines using the iterator_count() function.
$file = '/path/to/file.txt';
$lineCount = iterator_count(getLines($file)); // the number of lines in the file



Para solo contar las líneas usa:

$handle = fopen("file","r");
static $b = 0;
while($a = fgets($handle)) {
    $b++;
}
echo $b;