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

Bibliothèques et frameworks PHP Discussion :

[XML] Lecture de fichier XML


Sujet :

Bibliothèques et frameworks PHP

  1. #1
    Nouveau membre du Club
    Homme Profil pro
    Développeur Web
    Inscrit en
    Octobre 2012
    Messages
    48
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Développeur Web

    Informations forums :
    Inscription : Octobre 2012
    Messages : 48
    Points : 29
    Points
    29
    Par défaut [XML] Lecture de fichier XML
    Bonjour,

    Je me tourne vers vous car je cherche une solution à mon problème d'analyse de fichier XML. Solution que je trouve absolument pas grosse galère..

    Je dois analyser des fichiers assez important > 1gb et je possède un serveur mutualisé

    Mes analyses de fichier sont effectuées automatiquement via mon hébergement et les tâches planifiées en SSH.

    J'utilise XMLReader via PHP pour analyse des fichiers et SimpleXMLElement pour créer les arbres de mes noeuds au fur et à mesure pour simplifier l'analyse et la récupération des données.

    Mon problème est que les tâches planifiées en mutualisées sont limitées à 1h d'éxécution.. et je ne peux pour le moment pas passer sur un dédié car trop cher pour le moment et à priori j'ai au moins besoin de 3h pour lire le fichier complet


    J'ai donc trouvé une solution :

    Au premier lancement de la tâche j'ouvre le fichier et je l'analyse. Au bout de 55 minutes d'analyses je tue la tâche via un die() et j'enregistre la dernière valeur analysée dans ma base de données ce qui fait que j'ai un genre de checkpoint pour l'analyse suivante commençant 10minutes après.

    A partir de la seconde mise à jour je récupère la référence du dernier noeud id dans ma base de donnée et tant que je ne le rencontre pas, lors de la nouvelle lecture du fichier, je n'effectue pas de mise à jour. Seulement si le fichier est supérieur à 1gb je peux mettre 20 minutes voir plus à retrouver l'ancienne référence.. je perds donc un temps fou.



    Help :

    Est-il possible via Xmlreader d'utiliser une fonction de type movetoelement ou vers l'attribut prenant en compte ma dernière référence sans avoir à relire tout le fichier ? Ceci me ferait gagner un temps considérable.

    Dans le fichier ci-dessous le but serait moveToAttribute('3'); mais je pense mal le mettre en place...


    Voici un exemple de fichier XML que je dois analyser :
    Code xml : 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
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    <?xml version="1.0" encoding="utf-8"?>
     
    <PRODUITS>
     
      <PRODUIT id="1">
     
        <CAPACITE>4</CAPACITE>
     
        <LOGEMENT><![CDATA[Résidence]]></LOGEMENT>
     
        <HEBERGEMENT><![CDATA[Studio 4 personnes]]></HEBERGEMENT>
     
        <REGION><![CDATA[Alpes du Nord]]></REGION>
     
        <PAYS><![CDATA[France]]></PAYS>
     
        <CODEPOSTAL>73500</CODEPOSTAL>
      </PRODUIT>
     
      <PRODUIT id="2">
     
        <CAPACITE>4</CAPACITE>
     
        <LOGEMENT><![CDATA[Résidence]]></LOGEMENT>
     
        <HEBERGEMENT><![CDATA[Studio 4 personnes]]></HEBERGEMENT>
     
        <REGION><![CDATA[Alpes du Nord]]></REGION>
     
        <PAYS><![CDATA[France]]></PAYS>
     
        <CODEPOSTAL>73500</CODEPOSTAL>
      </PRODUIT>


    Voici comment j'analyse mes fichiers :
    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
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    <?php
    if(file_exists("$file_xml"))
    {
      $reader = new XMLReader();
      $reader->open("$file_xml");
     
      while($reader->read())
      {
        if( ($reader->name === 'PRODUIT') && ($reader->nodeType==XMLReader::ELEMENT) )
        {
        $node = new SimpleXMLElement($reader->readOuterXML());
     
     
        $id= mysql_real_escape_string(utf8_decode($node['id']));
     
              // Si il s'agit d'une seconde mise à jour et que la référence précédente n'est pas vide alors je test la référence et tant que je ne la retrouve pas je ne fais pas la mise à jour
              if( ($id!="0") && ($id!="3") ) 
          {
          // On ne fait rien le produit a déjà été enregistré
              }
          else
          { 
    // Sinon c'est bon on continu la mise à jour  
    $id = 0;    
    $num = mysql_real_escape_string(utf8_decode($node['num']));
              ....                          
          }
        }
       }
    $reader->close();
    }


    Si jamais cette fonction n'est pas possible, est-il possible de supprimer le noeud lu du fichier xml lu et ensuite de le sauvegarder en l'écrasant au bout de 50 mn ? Ceci permettant de retrouver directement le noeud où j'en étais sans avoir à bidouiller ?

    Merci pour votre aide.

    Guillaume

  2. #2
    Membre émérite Avatar de tsuji
    Inscrit en
    Octobre 2011
    Messages
    1 558
    Détails du profil
    Informations forums :
    Inscription : Octobre 2011
    Messages : 1 558
    Points : 2 736
    Points
    2 736
    Par défaut
    Ici, il s'agit de performance. Le problème de trace et d'utilisation de mémoire est réglé d'une grande partie par se servant de XMLReader. L'emploi de SimpleXML pour parser un fragment répeté de PRODUIT n'est pas nuisible en ce qui concerne la trace de mémoire mais il est pourtant absoluement en ce qui concerne performance pour un document de taille très importante. C'est là qu'il faut faire un effort d'y améliorer avant tout.

    Eliminez la partie ou SimpleXML intervient et ne utilisez que XMLReader. En plus, mysql_real_escape_string() et utf8_decode() sont coûteuses. Faites-les au dernier moment avant de faire intervenir mysql. Je pense recueillir les données dans un fichier et puis mettre à jour la bd, si c'est le but, en batch est envisageable et plus souhaîtable.

    Pour un document de taille de giga-byte, ces simples mesures vaut facilement une facteur de 40-50% d'amélioration en performance.

    Faites ceci et regardez si le progrès s'est fait court encore.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
        //... etc etc    
        while($reader->read()) {
            if( ($reader->name === 'PRODUIT') && ($reader->nodeType==XMLReader::ELEMENT)) {
     
                //$node = new SimpleXMLElement($reader->readOuterXML());
                //$id= mysql_real_escape_string(utf8_decode($node['id']));
     
                $id=$reader->getAttribute("id");
     
                //si absoluement nécessaire de faire à ce moment, sinon, ne le faites pas, c'est coûteux
                //$id= mysql_real_escape_string(utf8_decode($id));
     
                //etc etc...

  3. #3
    Nouveau membre du Club
    Homme Profil pro
    Développeur Web
    Inscrit en
    Octobre 2012
    Messages
    48
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Développeur Web

    Informations forums :
    Inscription : Octobre 2012
    Messages : 48
    Points : 29
    Points
    29
    Par défaut
    Bonjour !

    Merci pour le retour et l'aide !

    Ok super je vais essayer cette méthode pour voir si cela prend moins de mémoire.

    <produit id="15"></produit> -
    $id=$reader->getAttribute("id"); -> permet de récupérer l'id de produit soit 15

    Comment dans ce cas je récupère la valeur d'un champ normal soit ville
    <ville>Paris</ville> ?

    $ville=$reader->getAttribute("ville"); ?

    Merci encore.

    Guillaume

  4. #4
    Membre émérite Avatar de tsuji
    Inscrit en
    Octobre 2011
    Messages
    1 558
    Détails du profil
    Informations forums :
    Inscription : Octobre 2011
    Messages : 1 558
    Points : 2 736
    Points
    2 736
    Par défaut
    Ecoutez: si vous n'essayez pas assez de se familiariser avec les méthodes propres à XMLReader d'abord et de ce qu'il est capable de faire, il serait en vain d'esquisser des chemins d'optimisation par pure imagination. Ce que vous demandez comme comment d'obtenir la valeur de 'ville' (ce qui n'est pas apparu dans le xml que vous montrez, d'ailleurs) est possible bien entendu.

    Pour couper court la série de questions qui puisse s'enchaîner sans cesse, je peux vous montrer comment faire, tout en restant avec la structure du xml comme vous avez montrée, pour obtenir une sortie du format comme ça:
    Code text : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    id=1; LOGEMENT=Résidence; HEBERGEMENT=Studio 4 personnes; REGION=Alpes du Nord; PAYS=France; CODEPOSTAL=73500; 
    id=2; LOGEMENT=Résidence; HEBERGEMENT=Studio 4 personnes; REGION=Alpes du Nord; PAYS=France; CODEPOSTAL=73500; 
    ...
    Ce n'est pas le plus immédiatement utile mais vous pouvez le refaire envers une forme plus sql (comme insert ou update etc). Et avec ce genre de sortie, vous mettez à jour mysql en batch ou faites ce que vous voulez avec... au second temps.
    Code php : 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
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    if (file_exists($xmlfile)) {
        $reader = new XMLReader();
        $reader->open($xmlfile);
     
        while($reader->read()) {
            if( ($reader->name === 'PRODUIT') && ($reader->nodeType==XMLReader::ELEMENT)) {
     
                $id=$reader->getAttribute('id');
                echo 'id=',$id, "; ";
     
                while($reader->read()) {
                    switch ($reader->nodeType==XMLReader::ELEMENT) {
                        case true:
                            switch ($reader->name) {
                                case 'CAPACITY':
                                case 'LOGEMENT':
                                case 'HEBERGEMENT':
                                case 'REGION':
                                case 'PAYS':
                                case 'CODEPOSTAL':
                                    echo $reader->name,'=',$reader->readString(),"; ";
                                    break;
                                default:
                                    break;
                            }
                            break;
                        case false:
                            break;
                    }
                    if ($reader->name==='PRODUIT' && $reader->nodeType==XMLReader::END_ELEMENT) {
                        echo "\n";
                        break;
                    }
                }
            }
        }
     
        $reader->close();
    }
    Pour un document de taille de 100 MB, ça n'a besoin à peine de 2-3 mins, et pour la taille de 1 GB, j'estime que ça devrait finir dans une demi-heure sur un ordinateur raisonnable, ordinaire mais pas du tout à haute gamme de ce temps.

    Faites voir si ça fait un progrès assez grand ou pas?

  5. #5
    Nouveau membre du Club
    Homme Profil pro
    Développeur Web
    Inscrit en
    Octobre 2012
    Messages
    48
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Développeur Web

    Informations forums :
    Inscription : Octobre 2012
    Messages : 48
    Points : 29
    Points
    29
    Par défaut Merci Tsuji !!!!!
    Bonjour Tsuji,

    Merci encore une fois pour le retour.

    Navré pour le dérangement mais il est vrai que :
    1. Les sources sur xmlreader sont quasiment inexistantes
    2. Celles qui existent sont très mal expliquées.
    3. Quand on est en contact avec quelqu'un qui utilise bien et qui explique bien XmlReader et qu'on a encore des lacunes on a pas envie de le lacher encore merci à toi !!

    J'avais déjà essayé la technique que tu viens de me donner mais il est vrai que celle-ci ne fonctionnait pas car j'avais une boucle dans une boucle qui n'avait pas de fin donc gros problème.. j'avais tout simplement oublié le "break;"

    J'ai modifié complètement mon code en m'inspirant de ton exemple.

    Cette ligne de code en php est plus simple qu'un switch pour récupérer tous les champs du Xml. Le code que j'ai donné en exemple ne prenant que quelques noeuds.. Chaque produit du fichier original possède plus de 100noeuds d'où la complexité

    Code php : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
     
    <?php
    if($reader->nodeType==XMLReader::ELEMENT)
    {
    	$nom_variable = strtolower(trim($reader->name));
    	$$nom_variable = trim($reader->readString());
    }
    ?>

    A priori après la mise à jour et après quelques tests,je passe de plus de 7 heures de mise à jour à moins de 2h !!! L'optimisation semble avoir fonctionné.. j'ai laissé le tableau SimpleXMLElement de côté car il mangeait toute ma mémoire !!

    Encore merci à toi pour ta patience et ton aide

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

Discussions similaires

  1. lecture de fichier xml
    Par cyreel dans le forum VB 6 et antérieur
    Réponses: 3
    Dernier message: 28/12/2006, 16h06
  2. [DOM4J] Problème de lecture de fichier xml avec dom4j
    Par santana2006 dans le forum Format d'échange (XML, JSON...)
    Réponses: 3
    Dernier message: 05/04/2006, 16h52
  3. VA lors de lecture du fichier XML
    Par LN(a) dans le forum Langage
    Réponses: 6
    Dernier message: 14/09/2005, 23h43
  4. [vb.net][xml] probleme de lecture de fichier
    Par graphicsxp dans le forum Windows Forms
    Réponses: 4
    Dernier message: 27/07/2005, 11h51
  5. Lecture de fichier au format XML ??
    Par nico0007 dans le forum Langage
    Réponses: 1
    Dernier message: 07/07/2005, 15h40

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