104 votes

Efficace de compter le nombre de lignes d'un fichier texte. (200 mo+)

Je viens de constater que mon script me donne une erreur fatale:

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

Qu'est ceci:

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

Donc je pense que c'est la difficulté de charger le fichier en mémoire et en comptant le nombre de lignes, est-il un moyen plus efficace que je peux le faire sans avoir des problèmes de mémoire?

Les fichiers texte que j'ai besoin de compter le nombre de lignes de gamme à partir de 2 MO à 500 mo. Peut-être un Concert parfois.

Merci à tous pour votre aide.

177voto

Dominic Rodger Points 44489

Cela permettra d'utiliser moins de mémoire, car elle n'a pas charger tout le fichier en mémoire:

$file="largefile.txt";
$linecount = 0;
$handle = fopen($file, "r");
while(!feof($handle)){
  $line = fgets($handle);
  $linecount++;
}

fclose($handle);

echo $linecount;

fgets des charges d'une seule ligne dans la mémoire (si le deuxième argument $length est omis, il sera de garder la lecture à partir du flux jusqu'à ce qu'il atteigne la fin de la ligne, qui est ce que nous voulons). C'est encore rare d'être aussi rapide que d'utiliser autre chose que PHP, si vous vous souciez de la paroi du temps ainsi que de l'utilisation de la mémoire.

Le seul danger c'est si toutes les lignes sont particulièrement longue (que faire si vous rencontrez un fichier de 2 go sans les sauts de ligne?). Dans ce cas, vous êtes mieux de faire aspirer, il en en morceaux, et le comptage de fin de chaîne de caractères:

$file="largefile.txt";
$linecount = 0;
$handle = fopen($file, "r");
while(!feof($handle)){
  $line = fgets($handle, 4096);
  $linecount = $linecount + substr_count($line, PHP_EOL);
}

fclose($handle);

echo $linecount;

119voto

Jack Points 88446

À l'aide d'une boucle d' fgets() des appels est bien la solution et la plus simple à écrire, cependant:

  1. même si en interne, le fichier est lu à l'aide d'un tampon de 8192 octets, votre code d'appeler cette fonction pour chaque ligne.

  2. techniquement, il est possible qu'une seule ligne peut être plus grande que la mémoire disponible si vous êtes en train de lire un fichier binaire.

Ce code lit un fichier en blocs de 8 ko pour chaque et compte ensuite le nombre de retours à la ligne à l'intérieur de ce morceau.

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 durée moyenne de chaque ligne est à plus de 4 ko, vous pourrez déjà commencer à économiser sur les appels de fonction, et ceux-ci peuvent ajouter jusqu'à quand vous traitez de gros fichiers.

Référence

J'ai couru un test avec un fichier de 1 go; en voici les résultats:

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

Le temps est mesuré en secondes temps réel, voir ici ce qu'est le vrai moyen

37voto

Dave Sherohman Points 25122

Si vous êtes en cours d'exécution sur une Linux/Unix hôte, la solution la plus simple serait d'utiliser exec() ou similaire pour exécuter la commande wc -l $path. Assurez-vous simplement que vous avez épuré $path d'abord pour être sûr qu'il n'est pas quelque chose comme "/chemin/vers/fichier ; rm-rf /".

34voto

Andy Braham Points 958

Il y a un moyen plus rapide que j'ai trouvé qui ne nécessite pas de boucle dans le fichier entier

uniquement sur les systèmes *nix, il y a peut être une façon similaire sur windows ...

$file = '/path/to/your.file';

//Get number of lines
$totalLines = intval(exec("wc -l '$file'"));

9voto

Ben Harold Points 998

Si vous êtes à l'aide de PHP 5.5, vous pouvez utiliser un générateur. Cela permettra de ne PAS travailler dans n'importe quelle version de PHP avant de 5.5. À partir de php.net:

"Les générateurs de fournir un moyen facile à mettre en œuvre simple itérateurs sans la surcharge ou de la complexité de l'implémentation d'une classe qui implémente l'interface 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

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X