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

Python Discussion :

regex et xml


Sujet :

Python

  1. #1
    Membre éprouvé

    Profil pro
    Account Manager
    Inscrit en
    Décembre 2006
    Messages
    2 301
    Détails du profil
    Informations personnelles :
    Localisation : France, Savoie (Rhône Alpes)

    Informations professionnelles :
    Activité : Account Manager

    Informations forums :
    Inscription : Décembre 2006
    Messages : 2 301
    Par défaut regex et xml
    Bonjour,
    j'aimerais savoir comment récupérer ce qui est entre <attribut valeur = "5"> et </attribut> dans <attribut valeur = "5">Du texte ou autre chose, peu importe...</attribut>.

    J'imagine qu'il faut "matcher" des groupes.

    Bien entendu, je ne cherche pas à faire un "parseur" de XML, mais à mieux comprendre les regex.

  2. #2
    Membre Expert

    Homme Profil pro
    Diverses et multiples
    Inscrit en
    Mai 2008
    Messages
    662
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Diverses et multiples

    Informations forums :
    Inscription : Mai 2008
    Messages : 662
    Par défaut
    Héhéhé… Il se trouve que j’ai, il y a quelques années maintenant, implémenté un mini-parseur xml en regex (sous PHP, d’ailleurs…).

    Alors, en fait, il y a plusieurs situations*:

    * S’il ne peut pas y avoir d’autre élément <attribut> au sein de l’un d’eux, la regex est (très) simple, ’suffit de parcourir le contenu en mode non-glouton, quelque chose comme*:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    "<attribut ?[^>]*>(.*?)</attribut>"
    * Si les niveaux d’imbrication sont limités et restent raisonnables, ’suffit d’ajouter des imbrications optionnelles, c’est fastidieux mais pas trop compliqué non plus…

    * Par contre, si on veut pouvoir gérer des niveaux d’imbrication quelconque… C’est pas possible en pur regex Python (en tout cas, à ma connaissance)*! En PHP, c’est possible, grâce à une fonctionnalité avancée, les regex récursives… Mais ça donne un code franchement indigeste*! Je pourrai retrouver ce que j’avais pondu à l’époque, si ça t’intéresse…

  3. #3
    Membre éprouvé

    Profil pro
    Account Manager
    Inscrit en
    Décembre 2006
    Messages
    2 301
    Détails du profil
    Informations personnelles :
    Localisation : France, Savoie (Rhône Alpes)

    Informations professionnelles :
    Activité : Account Manager

    Informations forums :
    Inscription : Décembre 2006
    Messages : 2 301
    Par défaut
    Merci pour la réponse à ma question imprécise car j'ai omis de préciser que je ne connais pas "attribut" à l'avance...

  4. #4
    Membre Expert

    Homme Profil pro
    Diverses et multiples
    Inscrit en
    Mai 2008
    Messages
    662
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Diverses et multiples

    Informations forums :
    Inscription : Mai 2008
    Messages : 662
    Par défaut
    Ben, ça change pas grand chose, si*? Tu fais juste un .format() (ou un %… ) pour insérer le nom de ta balise dans le code de la regex, avant de la compiler*?

    Ou alors, tu veux faire un truc genre «*je veux le contenu de la première balise rencontrée, quelle qu’elle soit*» –*dans ce cas, il faut effectivement avoir recours à une substitution*:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    "<(?P<tag>\S+) ?[^>]*>(.*?)</(?P=tag)>"
    Ici, j’ai utilisé un groupe nommé pour récupérer le nom du tag, mais un groupe anonyme aurait aussi bien pu faire l’affaire (le nom du tag est constitué de n’importe quoi sauf des espaces).

  5. #5
    Membre éprouvé

    Profil pro
    Account Manager
    Inscrit en
    Décembre 2006
    Messages
    2 301
    Détails du profil
    Informations personnelles :
    Localisation : France, Savoie (Rhône Alpes)

    Informations professionnelles :
    Activité : Account Manager

    Informations forums :
    Inscription : Décembre 2006
    Messages : 2 301
    Par défaut
    Merci.

    Si je comprends bien, P<tag> permet de définir un nom que l'on réutilise ensuite dans P=tag.

  6. #6
    Membre Expert

    Homme Profil pro
    Diverses et multiples
    Inscrit en
    Mai 2008
    Messages
    662
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Diverses et multiples

    Informations forums :
    Inscription : Mai 2008
    Messages : 662
    Par défaut
    C’est ça, (?P<tag>…) définit un groupe nommé tag, que tu peux ensuite réutiliser dans la regex, ou récupérer dans l’objet match (par .groupdict()).

  7. #7
    Membre éprouvé

    Profil pro
    Account Manager
    Inscrit en
    Décembre 2006
    Messages
    2 301
    Détails du profil
    Informations personnelles :
    Localisation : France, Savoie (Rhône Alpes)

    Informations professionnelles :
    Activité : Account Manager

    Informations forums :
    Inscription : Décembre 2006
    Messages : 2 301
    Par défaut
    Merci.

    PS : peux-tu donner un exemple cour de regex récursive en PhP ?

  8. #8
    Membre Expert

    Homme Profil pro
    Diverses et multiples
    Inscrit en
    Mai 2008
    Messages
    662
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Diverses et multiples

    Informations forums :
    Inscription : Mai 2008
    Messages : 662
    Par défaut
    Ben, désolé, je ne suis pas sûr qu’on puisse en donner un exemple simple… En voici une pour récupérer tout le contenu du bloc le plus externe, dans une chaîne du genre*:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    "bla {lorem {ipsum} et {cae{tera}}} blablabla"
    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
    <?php
    $regex_simple = '/'
    	  // The initial open {.
    	. '\{'
    		  // Condition: If it is not a closing }…
    		. '(?P<content>(?(?!\})'
    			  // Condition: If it is an opening {, try the (recursive) sub-pattern matching…
    			. '(?(?=\{)'
    				  // The start of the recursive sub-pattern.
    				. '(?P<subpattern>'
    					  // A nested element of same kind as first opening one,
    					. '\{'
    						  // Condition: If it is not a closing tag…
    						. '(?(?!\})'
    							  // Condition: If it is an opening one, try recursive pattern…
    							. '(?(?=\})(?P>subpattern)'
    							  // Else, consume as much chars as possible, until the next
    							  // opening or closing tag…
    							. '|((?:.(?!\{)(?!\}))*.))'
    						  // And retest the “extern” condition.
    						. ')*'
    					  // The matching closing tag!
    					. '\}'
    				  // End of subpattern.
    				. ')'
    			  // Else, consume as much chars as possible, until the next
    			  // opening or closing tag.
    			. '|((?:.(?!\{)(?!\}))*.))'
    		  // And retest the whole “extern” condition.
    		. ')*)'
    	  // Out-most closing tag.
    	. '\}'
    	. '/s';

    En voici le synopsis*:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    On teste/détecte le { ouvrant.
        Tant que la suite n’est pas le } fermant (genre {}) (look-ahead):
            Si la suite est l’ouverture d’un nouveau bloc ({):
                Partie récursive de la regex, qui, en boucle:
                    * Consomme du contenu.
                    * “S’appelle” dès qu’elle détecte un nouveau { (look-ahead).
                    * “Retourne” dès qu’elle détecte un } (look-ahead).
            Sinon, on consomme le contenu, jusqu’au prochain { ou } (look-ahead).
    Et on consomme le dernier }!
    Pour info, voici ce que ça donnait pour du html ($tagname et $attributes sont deux sous-regex correspondant au tag et/ou attributs recherchés…)*:

    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
    40
    41
    42
    43
    // This regex will “parse” the piece of html code to extract the elements
    // we need. Note that it is not “fully armored” against strange or invalid html!
    // This a VERY complex recursive regex (took me hours to make it work!).
    // So lets detail it!
    // Note: About opening elements: when no tag name is given, all opening
    //       elements are tested. There seems to be a bug with elements
    //       like “<hr />” (returns a white page, without any exception…).
    //       So I added a negative look-ahead test to exclude these things
    //       explicitly (even though their matching test should fail nicely!).
    // WARNING: This seems to be a VERY sensible regex, prone to strange errors
    //          very quickly…
    $regex = '/'
    	  // The initial open element.
    	. '<(?P<tagname>'.$tagname.')(?! *\/>)'.$attributes.'\s*>'
    		  // Condition: If it is not a closing tag…
    		. '(?P<content>(?(?!<\/(?P=tagname)>)'
    			  // Condition: If it is an opening tag, try the (recursive) sub-pattern matching…
    			. '(?(?=<(?P=tagname)(?: [^>]*|)>)'
    				  // The start of the recursive sub-pattern.
    				. '(?P<subpattern>'
    					  // A nested element of same kind as first opening one,
    					. '<(?P=tagname)(?: [^>]*|)>'
    						  // Condition: If it is not a closing tag…
    						. '(?(?!<\/(?P=tagname)>)'
    							  // Condition: If it is an opening one, try recursive pattern…
    							. '(?(?=<(?P=tagname)(?: [^>]*|)>)(?P>subpattern)'
    							  // Else, consume as much chars as possible, until the next
    							  // opening or closing tag…
    							. '|((?:.(?!<(?P=tagname)(?: [^>]*|)>)(?!<\/(?P=tagname)>))*.))'
    						  // And retest the “extern” condition.
    						. ')*'
    					  // The matching closing tag!
    					. '<\/(?P=tagname)>'//.*?'
    				  // End of subpattern.
    				. ')'
    			  // Else, consume as much chars as possible, until the next
    			  // opening or closing tag.
    			. '|((?:.(?!<(?P=tagname)(?: [^>]*|)>)(?!<\/(?P=tagname)>))*.))'
    		  // And retest the whole “extern” condition.
    		. ')*)'
    	  // Out-most closing tag.
    	. '<\/(?P=tagname)>'
    	. '/s';


  9. #9
    Membre éprouvé

    Profil pro
    Account Manager
    Inscrit en
    Décembre 2006
    Messages
    2 301
    Détails du profil
    Informations personnelles :
    Localisation : France, Savoie (Rhône Alpes)

    Informations professionnelles :
    Activité : Account Manager

    Informations forums :
    Inscription : Décembre 2006
    Messages : 2 301
    Par défaut
    Merci.

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

Discussions similaires

  1. Regex et xml
    Par laurentapologic dans le forum Langage
    Réponses: 3
    Dernier message: 24/04/2008, 12h39
  2. [RegEx] Regex pour extraire des noeuds XML
    Par lagotonio dans le forum Langage
    Réponses: 6
    Dernier message: 18/04/2008, 15h52
  3. Réponses: 2
    Dernier message: 10/04/2007, 12h46
  4. regex dans fichier XML
    Par pouss dans le forum Format d'échange (XML, JSON...)
    Réponses: 3
    Dernier message: 10/10/2006, 10h35
  5. [débutant][Regex] changer "cage01.xml" en "cage N° 1
    Par pingoui dans le forum Format d'échange (XML, JSON...)
    Réponses: 2
    Dernier message: 14/09/2004, 14h05

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