IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Langage PHP Discussion :

fgetcsv et allocation de mémoire [PHP 5.4]


Sujet :

Langage PHP

  1. #1
    Membre habitué
    Homme Profil pro
    Inscrit en
    Décembre 2012
    Messages
    129
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Yvelines (Île de France)

    Informations forums :
    Inscription : Décembre 2012
    Messages : 129
    Points : 149
    Points
    149
    Par défaut fgetcsv et allocation de mémoire
    Bonjour tout le monde,

    Je sèche sur une affaire bien embêtante...

    J'ai une fonction qui marche bien sur les petits fichiers csv mais pas sur les gros, pour lesquels php me renvoi l'erreur suivante (ex : fichier de 8Mo) :
    Fatal error: Allowed memory size of 134217728 bytes exhausted (tried to allocate 72 bytes)
    Comment est-ce possible d'alloué 130Mo de mémoire pour lire un fichier de 8Mo ?

    Voici ma fonction :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
     
    if (($handle = fopen($fichier, "r")) !== false) {
    	$tmp = [];
        while (($data = fgetcsv($handle, 0, ";")) !== false) { // problème sur cette ligne
        	$tmp[count($tmp)] = $data;
        }
        fclose($handle);
        $this->TAB = $tmp;
    }else{
    	$this->error[3] = 'Fichier non disponible';
    }
    Je pense que le problème vient de la fonction fgetcsv et notamment de l'argument length :
    Doit être plus grand que la plus grande ligne (en terme de caractères) à lire dans le fichier (y compris le caractère de fin de ligne). Ce paramètre est optionnel depuis PHP 5. Omettre ce paramètre (ou le définir à 0 en PHP 5.1.0 et suivant) fait que la longueur maximale de la ligne n'est pas limitée, ce qui est légèrement plus lent.
    Mais que faire si l'on est dans l'impossibilité d'anticiper le nombre de caractère par ligne de notre fichier ?
    Quelqu'un pourrait-il m'aider à comprendre le fonctionnement sous-jacent à cette fonction ?

    Merci d'avance pour votre aide !

  2. #2
    Modérateur
    Avatar de sabotage
    Homme Profil pro
    Inscrit en
    Juillet 2005
    Messages
    29 208
    Détails du profil
    Informations personnelles :
    Sexe : Homme

    Informations forums :
    Inscription : Juillet 2005
    Messages : 29 208
    Points : 44 155
    Points
    44 155
    Par défaut
    On voit dans l'erreur que la lecture du CSV prend 72 octets par ligne.
    Tu n'as pas un traitement avant la lecture CSV ?
    Dans tous les cas il serait intéressant de regarder precisement la consommation de mémoire pendant le traitement.

    Au passage écris simplement :
    Il n'y a pas besoin de mesurer le tableau à chaque tour.

  3. #3
    Modératrice
    Avatar de Celira
    Femme Profil pro
    Développeuse PHP/Java
    Inscrit en
    Avril 2007
    Messages
    8 633
    Détails du profil
    Informations personnelles :
    Sexe : Femme
    Âge : 39
    Localisation : France

    Informations professionnelles :
    Activité : Développeuse PHP/Java
    Secteur : Industrie

    Informations forums :
    Inscription : Avril 2007
    Messages : 8 633
    Points : 16 372
    Points
    16 372
    Par défaut
    Concernant la question sur la longueur de la ligne : si ton fichier CSV sert à alimenter une base de donnée, tu connais la longueur maximale possible pour chaque champ en base de données. Tu as juste à faire la somme, et ajouter les séparateurs et les délimiteurs (le mieux étant de prévoir une marge de sécurité)

    Pour le reste, je plussoie Sab' : il serait intéressant de savoir ce que le traitement fait d'autre avant la lecture CSV, et à quoi sert le tableau $tmp au final.

  4. #4
    Membre habitué
    Homme Profil pro
    Inscrit en
    Décembre 2012
    Messages
    129
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Yvelines (Île de France)

    Informations forums :
    Inscription : Décembre 2012
    Messages : 129
    Points : 149
    Points
    149
    Par défaut
    Merci pour vos réponses et pour l'astuce !

    Avant cela le script télécharge le fichier csv en question sur un ftp distant, puis ouvre la version uploadé en local afin de le mettre dans ce tableau.

    Le tableau sert en aval à effectuer des contrôle AVANT un éventuel chargement dans une table qui sera créée dynamiquement à partir d'une partie du contenu du csv. Ceci afin de permettre de faire du reporting dessus, un peu comme les tableaux croisés dynamiques sur Excel.

    J'ai effectué ce test (pour info le fichier fais un peu moins de 45000 lignes pour 8Mo = 177 o par ligne en moyenne, pas 3Mo ?) :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
     
    if (($handle = fopen($fichier, "r")) !== false) {
    	$tmp = [];
    	$mem = 0;
        while (($data = fgetcsv($handle, 0, ";")) !== false) {
        	echo memory_get_usage().' (+'.intval(memory_get_usage()-$mem).')<br>';
        	$mem = memory_get_usage();
        	$tmp[] = $data;
        }
        fclose($handle);
        $this->TAB = $tmp;
    }else{
    	$this->error[3] = 'Fichier non disponible';
    }
    Et voici le log :
    419936 (+419968)
    423104 (+3200)
    426200 (+3128)
    429288 (+3120)
    [...] // je vous épargne les milliers de lignes
    134182264 (+3120)
    134185352 (+3120)
    134188440 (+3120)
    134191528 (+3120)

    Fatal error: Allowed memory size of 134217728 bytes exhausted (tried to allocate 72 bytes) in /var/www/factory/scripts/bibli.php on line 139

  5. #5
    Membre habitué
    Homme Profil pro
    Inscrit en
    Décembre 2012
    Messages
    129
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Yvelines (Île de France)

    Informations forums :
    Inscription : Décembre 2012
    Messages : 129
    Points : 149
    Points
    149
    Par défaut
    Double post pour précision :
    J'ai regardé à la main le fichier en question possède des lignes d'environ 250 caractères.
    De plus j'ai essayé de mettre le paramètre length à 1000, le log renvoi la même chose.

  6. #6
    Expert éminent Avatar de CosmoKnacki
    Homme Profil pro
    Justicier interdimensionnel
    Inscrit en
    Mars 2009
    Messages
    2 912
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Charente Maritime (Poitou Charente)

    Informations professionnelles :
    Activité : Justicier interdimensionnel

    Informations forums :
    Inscription : Mars 2009
    Messages : 2 912
    Points : 6 705
    Points
    6 705
    Par défaut
    Il faut voir que que mem_get_usage() ne dit pas tout, et que 8Mo de fichier texte ne fait pas 8Mo une fois transformé en tableau multidimensionnel. Les tableaux PHP sont plutôt gourmands en mémoire. Donc fgetcsv n'est pas en cause, mais bien ton monstrueux tableau multidimensionnel $tmp de 42000 items pour lequel PHP alloue une place de tous les diables.

    Solutions possibles:
    • Si ce que tu veux faire te le permet, ne stocke rien du tout et traite les lignes une par une dans la boucle while. C'est d'ailleurs le principe de fopen/fgetcsv qui utilise un flux, plutôt que de charger l'intégralité du fichier pour le saucissonner dans un deuxième temps (comme la solution file/str_getcsv).
    • Si tu utilises un sgbd, tu peux importer directement ton fichier csv dans une table, puis faire tes traitements dans la base (ce qui suivant ce que tu souhaites faire pourrait être plus rapide). Attention avec MySQL si tu as des champs entre quotes contenant des quotes échappés en étant doublés car il ne les gère pas correctement.
    • Au lieu d'utiliser les tableaux de base de PHP, utilise des SplFixedArray qui consomment moins de mémoire (~3X moins) et sont plus rapides (on peut adapter leur taille en cours de route au besoin).

  7. #7
    Membre habitué
    Homme Profil pro
    Inscrit en
    Décembre 2012
    Messages
    129
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Yvelines (Île de France)

    Informations forums :
    Inscription : Décembre 2012
    Messages : 129
    Points : 149
    Points
    149
    Par défaut
    Arf tu as raison CosmoKnacki en fait je pense que le problème vient du fait que ma class est mal construite.

    J'avais prévu une classe qui me créé un tableau de données, que je pouvais traité dans mon main en le mettant en paramètre des fonctions "trouver un champs", "tester les types" etc...

    Mais le mieux serait d'inclure ces méthodes dans la class et de travailler avec fopen/fgetcsv. Avec un constructeur qui télécharge le fichier dans un dossier temporaire et un destructeur qui supprime ce fichier.

    Je viens du VBA, qui est un langage très permissif, ce qui ne m'a pas incité à travailler les objets correctement :S

    Qu'en pensez-vous ?

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. [debutant] : Allocation de mémoire dynamique
    Par sam.fet dans le forum Langage
    Réponses: 5
    Dernier message: 15/02/2006, 15h58
  2. Problème d'allocation de mémoire dans la pile
    Par prophet666 dans le forum x86 32-bits / 64-bits
    Réponses: 6
    Dernier message: 19/01/2006, 03h22
  3. [Debutant]Allocation de mémoire
    Par gwendal84 dans le forum C
    Réponses: 6
    Dernier message: 07/12/2005, 20h04
  4. Double allocation de mémoire
    Par hunter001 dans le forum C++
    Réponses: 16
    Dernier message: 25/08/2005, 14h53
  5. pb d'allocation de mémoire
    Par shura dans le forum C
    Réponses: 7
    Dernier message: 17/04/2005, 22h10

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo