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 está teniendo dificultades para cargar el archivo en la memoria y contar el número de líneas, ¿hay alguna manera más eficiente de hacer esto sin tener problemas de memoria?
Los archivos de texto que necesito para contar el número de líneas varían de 2 MB a 500 MB. Quizás un concierto a veces.
Gracias a todos por cualquier ayuda.
\n
) siendo analizado en una máquina con Windows (PHP_EOL == '\r\n'
)fgets($handle, 1);
?substr_count()
, pero si tiene líneas muy largas, debe llamarwhile()
yfgets()
mucho más, lo que causa una desventaja. No lo olvides:fgets()
no lee línea por línea. Lee solo la cantidad de caracteres que definió$length
y si contiene un salto de línea, detiene lo que$length
se haya configurado.while(!feof())
hará que lea una línea adicional, porque el indicador EOF no se establece hasta después de que intente leer al final del archivo.$line = fgets($handle);
podría serfgets($handle);
porque$line
nunca se usa.fgets()
Sin embargo, usar un bucle de llamadas es una buena solución y la más sencilla de escribir:aunque internamente el archivo se lee usando un búfer de 8192 bytes, su código aún tiene que llamar a esa función para cada línea.
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 fragmentos de 8kB cada uno y luego cuenta el número de líneas nuevas 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 ahorrar en llamadas a funciones, y esas pueden sumarse cuando procese archivos grandes.
Punto de referencia
Ejecuté una prueba con un archivo de 1GB; aquí están los resultados:
El tiempo se mide en segundos en tiempo real, vea aquí lo que significa real
fuente
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 el método
PHP_INT_MAX
inSplFileObject::seek
.$file = new \SplFileObject('file.extension', 'r'); $file->seek(PHP_INT_MAX); echo $file->key() + 1;
fuente
wc -l
(debido a la bifurcación, supongo), especialmente en archivos pequeños.Si está ejecutando esto en un host Linux / Unix, la solución más fácil sería usar
exec()
o similar para ejecutar el comandowc -l $path
. Solo asegúrese de haber desinfectado$path
primero para asegurarse de que no sea algo como "/ ruta / a / archivo; rm -rf /".fuente
Hay una forma más rápida que encontré que no requiere recorrer todo el archivo
solo en sistemas * nix , puede haber una forma similar en Windows ...
$file = '/path/to/your.file'; //Get number of lines $totalLines = intval(exec("wc -l '$file'"));
fuente
exec('wc -l '.escapeshellarg($file).' 2>/dev/null')
Si está usando PHP 5.5, puede usar un generador . Sin embargo, esto NO funcionará en ninguna versión de PHP anterior a la 5.5. Desde php.net:
"Los generadores proporcionan una manera fácil de implementar iteradores simples sin la sobrecarga o la complejidad de implementar una clase que implementa 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
fuente
try
/finally
no es estrictamente necesario, PHP cerrará automáticamente el archivo por usted. Probablemente también debería mencionar que el conteo real se puede hacer usandoiterator_count(getFiles($file))
:)Esta es una adición a la solución de Wallace de Souza
También omite líneas vacías mientras cuenta:
function getLines($file) { $file = new \SplFileObject($file, 'r'); $file->setFlags(SplFileObject::READ_AHEAD | SplFileObject::SKIP_EMPTY | SplFileObject::DROP_NEW_LINE); $file->seek(PHP_INT_MAX); return $file->key() + 1; }
fuente
Si está bajo Linux, simplemente puede hacer:
number_of_lines = intval(trim(shell_exec("wc -l ".$file_name." | awk '{print $1}'")));
Solo tiene que encontrar el comando correcto si está usando otro sistema operativo
Saludos
fuente
private static function lineCount($file) { $linecount = 0; $handle = fopen($file, "r"); while(!feof($handle)){ if (fgets($handle) !== false) { $linecount++; } } fclose($handle); return $linecount; }
Quería agregar una pequeña corrección a la función anterior ...
en un ejemplo específico en el que tenía un archivo que contenía la palabra 'prueba', la función devolvió 2 como resultado. así que necesitaba agregar un cheque si fgets devuelve falso o no :)
que te diviertas :)
fuente
Basado en la solución de dominic Rodger, esto es lo que uso (usa wc si está disponible, de lo contrario, es una alternativa a la solución de dominic Rodger).
class FileTool { public static function getNbLines($file) { $linecount = 0; $m = exec('which wc'); if ('' !== $m) { $cmd = 'wc -l < "' . str_replace('"', '\\"', $file) . '"'; $n = exec($cmd); return (int)$n + 1; } $handle = fopen($file, "r"); while (!feof($handle)) { $line = fgets($handle); $linecount++; } fclose($handle); return $linecount; } }
https://github.com/lingtalfi/Bat/blob/master/FileTool.php
fuente
Se puede contar el número de líneas mediante los siguientes códigos:
<?php $fp= fopen("myfile.txt", "r"); $count=0; while($line = fgetss($fp)) // fgetss() is used to get a line from a file ignoring html tags $count++; echo "Total number of lines are ".$count; fclose($fp); ?>
fuente
Tienes varias opciones. La primera es aumentar la memoria disponible permitida, que probablemente no sea la mejor manera de hacer las cosas, dado que indica 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.
fuente
Hay otra respuesta que pensé que podría ser una buena adición a esta lista.
Si ha
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 de archivos creados por Unix o Windows.
DOS desventajas (al menos):
1) No es una buena idea que su script dependa tanto del sistema en el que se está ejecutando (puede que no sea seguro asumir que Perl y wc están disponibles)
2) Solo un pequeño error al escapar y ha entregado el acceso a un caparazón en su máquina.
Como con la mayoría de las cosas que sé (o creo que sé) sobre codificación, obtuve esta información de otro lugar:
Artículo de John Reeve
fuente
public function quickAndDirtyLineCounter() { echo "<table>"; $folders = ['C:\wamp\www\qa\abcfolder\', ]; foreach ($folders as $folder) { $files = scandir($folder); foreach ($files as $file) { if($file == '.' || $file == '..' || !file_exists($folder.'\\'.$file)){ continue; } $handle = fopen($folder.'/'.$file, "r"); $linecount = 0; while(!feof($handle)){ if(is_bool($handle)){break;} $line = fgets($handle); $linecount++; } fclose($handle); echo "<tr><td>" . $folder . "</td><td>" . $file . "</td><td>" . $linecount . "</td></tr>"; } } echo "</table>"; }
fuente
Utilizo este método para simplemente contar cuántas líneas en un archivo. ¿Cuál es la desventaja de hacer esto frente a las otras respuestas? Veo muchas líneas en comparación con 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;
fuente
La solución multiplataforma más sucinta que solo almacena una línea a la vez.
$file = new \SplFileObject(__FILE__); $file->setFlags($file::READ_AHEAD); $lines = iterator_count($file);
Desafortunadamente, tenemos que establecer la
READ_AHEAD
bandera de lo contrario seiterator_count
bloquea indefinidamente. De lo contrario, esto sería una sola línea.fuente
Para solo contar las líneas, use:
$handle = fopen("file","r"); static $b = 0; while($a = fgets($handle)) { $b++; } echo $b;
fuente