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 :

traiter un fichier CSV par morceau pour moins utiliser de mémoire


Sujet :

Langage PHP

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Membre Expert
    Avatar de laurentSc
    Homme Profil pro
    Webmaster débutant perpétuel !
    Inscrit en
    Octobre 2006
    Messages
    10 493
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 61
    Localisation : France, Isère (Rhône Alpes)

    Informations professionnelles :
    Activité : Webmaster débutant perpétuel !
    Secteur : Industrie

    Informations forums :
    Inscription : Octobre 2006
    Messages : 10 493
    Billets dans le blog
    1
    Par défaut traiter un fichier CSV par morceau pour moins utiliser de mémoire
    Bonjour,

    mon application lit des fichiers CSV afin d'en traiter les données.

    Je lis en entier le fichier et stocke toutes les données dans un tableau. Le principe fonctionne bien sur des petits fichiers, mais pour traiter un fichier qui pèse 32 mO, je tombe sur une limite d'utilisation de la mémoire
    Fatal error: Allowed memory size of 536870912 bytes exhausted (tried to allocate 335544320 bytes) in C:\projets\ticket_rawsrc\vendor\rawsrc\pdoplusplus\PDOPlusPlus.php on line 1296
    , mais la limite fixée dans php.ini (memory_limit) est atteinte (comme je suis en local, je peux la modifier ; je l'ai mise à 15000 mO (ce qui est monstrueux mais visiblement, ça suffit pas)). Pour gérer ce problème mon idée est de limiter la quantité de données lues dans le fichier, donc de traiter cette lecture par morceaux, donc de ne lire qu'un morceau, le traiter, puis lire un autre morceau, le traiter, également, et ainsi jusqu'à atteindre la fin du fichier CSV. Peut-on s'y prendre ainsi, et si oui, comment ?

  2. #2
    Membre Expert Avatar de vttman
    Homme Profil pro
    Développeur "couteau mosellan"
    Inscrit en
    Décembre 2002
    Messages
    1 140
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Moselle (Lorraine)

    Informations professionnelles :
    Activité : Développeur "couteau mosellan"
    Secteur : Industrie

    Informations forums :
    Inscription : Décembre 2002
    Messages : 1 140
    Par défaut
    Bonjour,
    Je ne vois pas trop le soucis techniquement parlant LaurentSc ?
    Voici ma proposition ...
    A voir aussi l'intérêt de stocker des informations par paquet de n lignes dans un tableau ?


    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
     
    Maxtab = 1000
    i=0 
    init montableau de 1000
    ouvrir csv 
    lire csv  
    tant que non EOF du csv
     i++
     tant que i <= Maxtab
      csv -> tab(i)
       i++
     fin tant que
     traitement tab par 1000 éléments (tab(1) -> tab(1000))
     init montableau de 1000
     i=0 
     lire csv  
    fin tant que
    fermeture csv

  3. #3
    Membre expérimenté
    Homme Profil pro
    Webdesigner
    Inscrit en
    Juin 2014
    Messages
    461
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 65
    Localisation : France, Hautes Pyrénées (Midi Pyrénées)

    Informations professionnelles :
    Activité : Webdesigner
    Secteur : Associations - ONG

    Informations forums :
    Inscription : Juin 2014
    Messages : 461
    Par défaut
    Bonjour.
    La quantité de mémoire utilisée n'est pas normale, même en ouvrant un fichier de 32MO.
    Je te suggère de placer à diverses étapes de ton traitement l'affichage de la mémoire utilisée par :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    echo memory_get_usage();
    Il doit y avoir des variables que tu peux détruire ou réinitialiser.

  4. #4
    Modérateur
    Avatar de grunk
    Homme Profil pro
    Lead dév - Architecte
    Inscrit en
    Août 2003
    Messages
    6 693
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 41
    Localisation : France, Côte d'Or (Bourgogne)

    Informations professionnelles :
    Activité : Lead dév - Architecte
    Secteur : Industrie

    Informations forums :
    Inscription : Août 2003
    Messages : 6 693
    Par défaut
    Il suffit de lire le fichier ligne par ligne et de traiter les lignes au fur et à mesure.
    Ex :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    $file = fopen('mon_fichier.csv', 'r');
    while (($line = fgetcsv($file)) !== FALSE) {
       var_dump($line);
    }
    fclose($file);
    Si chaque ligne doit donner lieu à une modification dans la base de données , il faudra alors envisager soit de grouper les insertions par lot (par exemple garder en mémoire 100 résultats et insérer une fois) ou passer par des transactions SQL qui devront elles aussi être groupées par lot.
    Pry Framework php5 | N'oubliez pas de consulter les FAQ Java et les cours et tutoriels Java

  5. #5
    Membre Expert
    Avatar de laurentSc
    Homme Profil pro
    Webmaster débutant perpétuel !
    Inscrit en
    Octobre 2006
    Messages
    10 493
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 61
    Localisation : France, Isère (Rhône Alpes)

    Informations professionnelles :
    Activité : Webmaster débutant perpétuel !
    Secteur : Industrie

    Informations forums :
    Inscription : Octobre 2006
    Messages : 10 493
    Billets dans le blog
    1
    Par défaut
    @vttman : OK. J'avais pas pensé qu'on pouvait ne lire que quelques lignes, faire un traitement puis reprendre la lecture...

    @domi65 : je vais faire ça, car OK, la consommation de mémoire est disproportionnée par rapport à la taille du tableau.

    @grunk : certes, l'énorme consommation de mémoire vient très probablement d'une mauvaise conception du traitement mais bon....
    J'étais parti avec cette conception :
    Code php : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    //1-lecture ligne par ligne du fichier et stockage des données dans le tableau self::$data
    while (($row = fgetcsv(self::$handle, $buffer, self::$separator, $enclosure,"")) !== false) {
                    self::$data[] = array_combine(self::$header, $row);
     
    //2- stockage du tableau dans une variable :
    $app_data = CSVImport::getData(); 
     
    //3- traitement du tableau stocké dans la variable :
    foreach ($app_data as $line_csv) //pour chq ligne, on l'analyse et si correcte, en bdd
            {
    ...
            }
    Je vais tout d'abord suivre le conseil de domi65 mais de toute façon adopter la proposition de Grunk : au lieu de stocker chaque ligne dans un tableau puis le traiter, après la lecture d'une ligne, traiter celle-ci tout de suite puis passer à la suivante.

  6. #6
    Membre Expert
    Avatar de laurentSc
    Homme Profil pro
    Webmaster débutant perpétuel !
    Inscrit en
    Octobre 2006
    Messages
    10 493
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 61
    Localisation : France, Isère (Rhône Alpes)

    Informations professionnelles :
    Activité : Webmaster débutant perpétuel !
    Secteur : Industrie

    Informations forums :
    Inscription : Octobre 2006
    Messages : 10 493
    Billets dans le blog
    1
    Par défaut
    Je viens de calculer ma consommation de mémoire avant et après la lecture du fichier :
    Code php : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
                $mem = memory_get_usage();
                echo "( memory avant lecture :".memory_get_usage()."<br/>";
     
                while (($row = fgetcsv(self::$handle, $buffer, self::$separator, $enclosure,"")) !== false) {
     
                    self::$data[] = array_combine(self::$header, $row);
                } 
     
                $mem = memory_get_usage();
                echo "memory après lecture :".memory_get_usage()."<br/>";
    et on passe de 937 672 octets à 252 009 440. Vu qu'il n'y a aucun traitement (seulement de la lecture puis stockage dans un tableau et que le fichier lu pèse 32 276kO). Y a pas un problème ?

  7. #7
    Membre Expert
    Avatar de laurentSc
    Homme Profil pro
    Webmaster débutant perpétuel !
    Inscrit en
    Octobre 2006
    Messages
    10 493
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 61
    Localisation : France, Isère (Rhône Alpes)

    Informations professionnelles :
    Activité : Webmaster débutant perpétuel !
    Secteur : Industrie

    Informations forums :
    Inscription : Octobre 2006
    Messages : 10 493
    Billets dans le blog
    1
    Par défaut
    Bonsoir,
    j'ai appliqué, non sans mal, l'approche proposée par grunk (donc je traite chaque ligne après l'avoir lue, plutôt que la stocker dans un tableau) et ça a l'air de moins solliciter la mémoire car j'ai jamais été si loin : dans le fichier CSV, il y a plus de 109000 lignes et pour l'instant, il y en a 35000 de traitées (donc analyse puis écriture en bdd) alors qu'avant, ça n'atteignait pas les 10000 lignes. Je vais le laisser tourner toute la nuit et on verra demain (OK, conception pourrie car pour un expert, ça serait plié en 10 secondes, alors que ça doit faire 15 minutes que ça tourne, mais bon...)

Discussions similaires

  1. buffer "par morceau" pour gros fichiers binaires
    Par Benoit_T dans le forum Langage
    Réponses: 11
    Dernier message: 09/11/2009, 10h58
  2. Réponses: 5
    Dernier message: 18/10/2008, 02h59
  3. [Macro] ouverture d'un fichier csv par macro différent du double-clic
    Par Caro-Line dans le forum Macros et VBA Excel
    Réponses: 1
    Dernier message: 19/04/2007, 16h36
  4. Réponses: 2
    Dernier message: 13/03/2007, 11h19

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