PHP : lire un fichier d'un gigaoctet ligne par ligne sans crash

Formateur Python en ligne pour débutants

Apprenez Python facilement sans surcharge théorique. Résolvez des tâches pratiques avec vérification automatique et écrivez du code directement dans le navigateur.

Start Course

Introduction : quand le simple file_get_contents tue le serveur

\n

Imaginez la situation : vous écrivez un analyseur de logs, un importateur CSV avec des millions de lignes ou vous traitez un dump de base de données. Vous écrivez $data = file_get_contents('bigfile.log'); — et votre script PHP plante avec l'erreur Fatal error: Allowed memory size exhausted. Familier ? Si non, vous avez de la chance. Mais tôt ou tard, chaque développeur est confronté à la tâche : « Comment lire un gros fichier ligne par ligne sans tuer le serveur ? ».

\n

Sur Stack Overflow, la question « How to read a large file line by line? » a recueilli des centaines de milliers de vues. Et ce n'est pas un hasard. Dans cet article, nous n'allons pas aborder une seule méthode, mais plusieurs approches — de la plus basique à la plus avancée. Vous apprendrez à lire des fichiers de plusieurs dizaines de gigaoctets en utilisant seulement quelques mégaoctets de RAM. C'est parti !

\n\n

Pourquoi file_get_contents et file() sont nuisibles pour les gros fichiers

\n

Avant de plonger dans l'optimisation, voyons pourquoi les fonctions standard ne conviennent pas.

\n
    \n
  • file_get_contents() — charge tout le fichier dans une chaîne. Si le fichier pèse 2 Go, PHP tentera d'allouer 2 Go de RAM. Sur un hébergement avec une limite memory_limit de 128 Mo, c'est une erreur fatale garantie.
  • \n
  • file() — lit également tout le fichier, mais le divise en plus en un tableau de lignes. La consommation mémoire augmente encore plus (plus les frais généraux du tableau).
  • \n
\n

Morale : si le fichier est plus grand que 10-20 % de votre memory_limit — oubliez ces fonctions. Nous avons besoin d'une approche par flux.

\n\n

Méthode 1 : La classique — fgets() et le descripteur de fichier

\n

La méthode la plus basique et fiable — ouvrir le fichier avec fopen() et le lire ligne par ligne avec fgets(). Cette approche utilise le tampon interne de PHP, qui est généralement de 8 Ko, et ne charge pas tout le fichier en mémoire.

\n\n
<?php\n$handle = fopen('bigfile.log', 'r');\nif ($handle) {\n    while (($line = fgets($handle)) !== false) {\n        // Traiter la ligne\n        echo $line; // ou écrire dans la BDD, ou compter quelque chose\n    }\n    fclose($handle);\n} else {\n    // Erreur d'ouverture du fichier\n    echo "Impossible d'ouvrir le fichier.";\n}\n?>
\n\n

Avantages : consommation mémoire minimale, fonctionne sur n'importe quelle version de PHP.

\n

Inconvénients : il faut fermer explicitement le fichier, pas de méthodes pratiques pour sauter les en-têtes ou itérer sur les lignes avec comptage.

\n

Ce code survivra à un fichier de n'importe quelle taille, à condition de ne pas essayer de stocker toutes les lignes dans un tableau à l'intérieur de la boucle.

\n\n

Méthode 2 : SplFileObject — une approche orientée objet

\n

Depuis PHP 5.1, nous avons l'excellente classe SplFileObject. Elle implémente les interfaces Iterator et SeekableIterator, ce qui la rend idéale pour la lecture ligne par ligne.

\n\n
<?php\n$file = new SplFileObject('bigfile.log');\nwhile (!$file->eof()) {\n    $line = $file->fgets();\n    // Traiter la ligne\n    echo $line;\n}\n?>
\n\n

Mais attention ! SplFileObject par défaut peut aussi charger les lignes en mémoire si vous utilisez current() dans un foreach. Pour garantir une faible consommation, utilisez fgets() dans une boucle while, comme montré ci-dessus.

\n\n

Une version plus avancée — réinitialiser les drapeaux pour l'optimisation :

\n\n
<?php\n$file = new SplFileObject('bigfile.log');\n$file->setFlags(SplFileObject::DROP_NEW_LINE); // Supprime \\n de la ligne\n\nforeach ($file as $lineNum => $line) {\n    // $lineNum — numéro de ligne (commence à 0)\n    echo "Ligne $lineNum : $line\\n";\n}\n?>
\n\n

Important : En utilisant foreach avec SplFileObject, PHP lit toujours une ligne à la fois. La mémoire n'est pas affectée. Mais si vous essayez d'appeler $file->seek(1000000) et de sauter dans le fichier — soyez prudent : seek parcourt

Blogs

Book Recommendations