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 :

Simplifier un test


Sujet :

Python

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  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 Simplifier un test
    Bonjour,
    j'aurais voulu savoir si Python permettait de faire le test ci-dessous de façon plus élégante.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    while ('_' in lesatoms) or ('_{' in lesatoms)
    or ('_{{' in lesatoms) or ('^{' in lesatoms) or ('^{{' in lesatoms):

  2. #2
    Expert confirmé
    Avatar de tyrtamos
    Homme Profil pro
    Retraité
    Inscrit en
    Décembre 2007
    Messages
    4 486
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Var (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Retraité

    Informations forums :
    Inscription : Décembre 2007
    Messages : 4 486
    Billets dans le blog
    6
    Par défaut
    Bonsoir,

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
     
    import re
     
    if re.search(r"_|_{|_{{|\^{|\^{{", lesatoms)!=None:
        # c'est Ok!
    Commentaire sur cette abominable expression régulière:
    - le "|" est un "ou"
    - le "^" étant un caractère spécial, il doit être préfixé par "\"
    - le reste est composé par tes 5 sous-chaines recherchées

    Et joyeux Noël!

    Tyrtamos

  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 ce joli cadeau.

    Joyeux noël à toi aussi.

  4. #4
    Membre Expert
    Profil pro
    Inscrit en
    Janvier 2007
    Messages
    1 418
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Janvier 2007
    Messages : 1 418
    Par défaut
    Je me suis amusé à chercher si on pouvait trouver une regex plus "élégante" encore.

    J'ai voulu grouper dans une seule RE1 les cas '_' et '_{' et '_{{'
    et dans une autre RE2 les cas '^{' et '^{{'
    Car il me semble que RE1|RE2 est une regex plus satisfaisante que RE1|RE2|RE3|RE4|RE5, relativement au nombre d'essais de matching, et donc à la vitesse d'exécution, effectués par le programme en utilisant une regex composée de sous-regex.
    J'ai donc d'abord écrit de façon simpliste pour RE1:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    import re
    bla = re.compile('_{{0,2}')
    ch = raw_input(' Entrer la chaine a tester : ')
    if bla.search(ch):
        print 'il y a'
    else:
        print 'rien trouvé'
    Or en testant je me suis aperçu que les chaines
    wc___jfk et wc_{{{{{jfk
    donnent évidemment un résultat 'il y a' .
    Dans le premier cas, le troisième underscore _ est détecté comme '_' suivi de zéro '{'
    et dans le second cas les '{' au delà du deuxième sont ignorés.

    J'ai alors réalisé que la condition while présentée par rambc cherche sans doute à détecter 5 et seulement 5 motifs précis:
    underscore_ unique suivi de 0 ou 1 ou 2 {
    et
    chapeau^ unique suivi de 1 ou 2 {
    et vraisemblablement aucun caractère _ ou ^ ou { devant le premier caractère.

    J'ai poursuivi sur cette base.
    Ce qui soit dit en passant entraine que la condition de rambc elle-même n'est pas correcte relativement à cet objectif (si c'est bien celui-ci).

    En effet
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    while ('_' in lesatoms) or ('_{' in lesatoms) or ('_{{' in lesatoms)
    est redondant:
    si '_{{' est dans lesatoms, il suffit de chercher'_'
    ou dit autrement: si on détecte '_' , inutile de chercher '_{{' .

    --------------------

    Donc j'ai cherché comment spécifier par une regex unique l'ensemble de ces 5 cas et uniquement ceux-ci.

    J'ai d'abord cherché pour les 3 cas '_' et '_{' et '_{{'

    J'ai eu du mal à trouver, principalement à cause du fait qu'il faut une regex qui détecte un '_' solitaire.
    Pour spécifier qu'un '_' n'est ni précédé ni suivi d'un autre '_' ,
    j'ai d'abord pensé que
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    bla = re.compile('[^_]_[^_]')
    suffisait.
    [^_] spécifie un ensemble composé de tous les caractères à l'exclusion de '_'
    Je pensais qu'après c'était fastoche:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    bla = re.compile('[^_]_[^_]{{0,2}[^{]')
    Mais ça ne marche pas, notamment si le motif cherché est en début ou en fin de chaine.
    Ce qui complique aussi beaucoup l'écriture de la regex, c'est entre le '_' et les 0 ou 1 ou 2 caractèes '{'.

    C'était une impasse. Parce que je cherchais à spécifier l'absence d'un caractère précis avec des expressions [^_] ou [^{] représentant un caractère.

    On ne s'en sort finalement qu'avec

    (?<!...)
    Matches if the current position in the string is not preceded by a match for .... This is called a negative lookbehind assertion. Similar to positive lookbehind assertions, the contained pattern must only match strings of some fixed length. Patterns which start with negative lookbehind assertions may match at the beginning of the string being searched.

    (?!...)
    Matches if ... doesn't match next. This is a negative lookahead assertion. For example, Isaac (?!Asimov) will match 'Isaac ' only if it's not followed by 'Asimov'.


    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    import re
    bla = re.compile('(?<!_)_(?!_){?{?(?!{)')
    while 1:
        ch = raw_input(' Entrer la chaine a tester : ')
        if bla.search(ch):
            print 'il y a'
        else:
            print 'rien trouvé'
    Nota: je préfère finalement '{?{?' à {{0,2}


    Pour les deux autres motifs
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    import re
    bla = re.compile('(?<!\^)\^(?!\^){{?(?!{)')
    while 1:
        ch = raw_input(' Entrer la chaine a tester : ')
        if bla.search(ch):
            print 'il y a'
        else:
            print 'rien trouvé'
    Il faut écrire '\^' car '^' est un caractère spécial.


    ----

    Ensuite, quand j'ai voulu regrouper les deux RE1 et RE2, je me suis dit qu'il faut sans doute aussi éviter les motifs
    '^_{'
    '_^{{'
    etc.
    Donc il faut mettre des ensembles[_^] dans les lookaround assertions.
    Ce qui donne:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    bla = re.compile('(?<![_^])_(?![_^]){?{?(?!{)|(?<![_^])\^(?![_^]){{?(?!{)')
    Il ne faut pas de '\' devant '^' dans un ensemble: entre '[' et ']' , les caractères spéciaux perdent leur nature spéciale.
    À ceci près qu'il faut quand même écrire [_^] et non pas [^_] car [^_] est interprété comme l'ensemble de tous les caractères à l'exception de l'underscore. Ce qui fait que (?![^_]) serait interprété comme: «ne devant pas être suivi de[tous les caractères sauf l'underscore]» ce qui est équivalent à «peut être suivi de l'underscore» c'est à dire le résultat inverse de ce qu'on veut !


    Je ne sais pas si on peut considérer que la regex composée ci-dessus est plus "élégante".
    Mais je pense qu'elle répond mieux à ce que tu cherches , rambc, que ta condition qui, je crois, n'est elle même pas correcte pour ce qu'il me semble que tu veux faire.
    En tous cas je me suis bien amusé et j'ai bien appris.

    J'ai mis tout ça sous forme de programme permettant de vérifier la validité de la regex composée sur des exemples de chaines:

    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
    import re
    li = ['ab_wt','ab_{wt','ab_{{wt','_wt','_{wt','_{{wt','ab_','ab_{','ab_{{',\
          'ab^wt','ab^{wt','ab^{{wt','^wt','^{wt','^{{wt','ab^','ab^{','ab^{{',\
          'saut',\
          'gh_{{{kj','_{{{kj','gh_{{{',\
          'gh^{{{kj','^{{{kj','gh^{{{',\
          'gh^_kj','gh^_{kj','gh^_{{kj','^_kj','^_{kj','^_{{kj','gh^_','gh^_{','gh^_{{',\
          'gh^_{{{kj','^_{{{kj','gh^_{{{',\
          'gh^^kj','gh^^{kj','gh^^{{kj','^^kj','^^{kj','^^{{kj','gh^^','gh^^{','gh^^{{',\
          'gh^^{{{kj','^^{{{kj','gh^^{{{',\
          'gh_^kj','gh_^{kj','gh_^{{kj','_^kj','_^{kj','_^{{kj','gh_^','gh_^{','gh_^{{',\
          'gh_^{{{kj','_^{{{kj','gh_^{{{',\
                'gh__kj','gh__{kj','gh__{{kj','__kj','__{kj','__{{kj','gh__','gh__{','gh__{{',\
          'gh__{{{kj','__{{{kj','gh__{{{']
     
    bla = re.compile(r'(?<![_^])_(?![_^]){?{?(?!{)|(?<![_^])\^(?![_^]){{?(?!{)')
    for u in li:
        if bla.search(u):
            print u+(10-len(u))*' ',' il y a'
        elif u=='saut':
            print
        else:
            print u+(10-len(u))*' ',' rien trouvé'
    -------

    Questions:

    - pourquoi peut on écrire un caractère '{' dans une regex sans le faire précéder de '\' alors que c'est un caractère spécial qui intervient dans les expressions : 'b{3,8}' signifiant 3 à 8 caractères 'b' à la suite.

    - b{1} ne signifie pas «caractère b une seule et unique fois».
    À mon avis , l'expression {1} ne sert jamais à rien, les caractères '{' et '}' ne servent que pour spécifier des répétitions. Qu'en pensez vous ?

    - à quoi sert le 'r' dans la regex re.search(r"_|_{|_{{|\^{|\^{{", lesatoms) ?
    où peut on trouver une explication sur ce genre de caractères ?

    - y a-t-il une raison qui rendrait préférable l'une ou l'autre des expressions appremment équivalentes {?{? et {{0,2} ?

  5. #5
    Expert confirmé
    Avatar de tyrtamos
    Homme Profil pro
    Retraité
    Inscrit en
    Décembre 2007
    Messages
    4 486
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Var (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Retraité

    Informations forums :
    Inscription : Décembre 2007
    Messages : 4 486
    Billets dans le blog
    6
    Par défaut
    Bonjour,

    J'ai essayé de répondre strictement à la question sans la remettre en cause. Mais il est vrai que la question comporte des redondances:

    - pour trouver '_' ou '_{' ou '_{{', il suffit de chercher '_'
    - pour trouver '^{' ou '^{{', il suffit de chercher '^{'

    Ce qui fait qu'on obtient le même résultat avec:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    if re.search(r"_|\^{", lesatoms)!=None:
    Et là, il me semble difficile de faire plus court...

    Il pourrait cependant y avoir une raison de conserver les 5 sous-chaines avec les expressions régulières: si, en plus de savoir qu'on en a trouvé une, on veut savoir laquelle!

    Mais dans ce cas, il faut changer l'ordre des sous-chaines, parce que si '_' est avant '_{', on ne trouvera jamais la 2ème. Il faut donc inverser, et mettre dans l'ordre:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    '_{{'  puis  '_{'  puis  '_'  puis  '\^{{'  et enfin '\^{'
    Dans ce cas, voilà ce que ça donnera:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
     
    x = re.search(r"_{{|_{|_|\^{{|\^{", lesatoms)
    if x!=None:
        print "ok"
        print x.group(), x.start(), x.end()  
    else:
        print "non"
    x.group() donnera la sous-chaine trouvée
    x.start() donnera l'indice du 1er caractère de la sous-chaine trouvée dans lesatoms
    x.end() donnera l'indice de fin

    On aura donc:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    x.group() == lesatoms[x.start():x.end()]
    Pour le reste de tes questions, j'ai seulement un chaleureux conseil: utilise TOUJOURS le r devant les expressions régulières. Cela dit à python qu'il s'agit d'une chaine brute, à transmettre sans aucune interprétation. En particulier s'il y a un '\'. En l'absence de ce 'r', il faudrait (pour reprendre l'exemple cité):

    - préfixer le '^' par '\' pour que le caractère '^' soit effectivement recherché
    - préfixer le '\' par un 2ème '\' pour que le '\' arrive effectivement à re.search !!!

    Je vois que tu t'attaques aux expressions régulières le "couteau entre les dents", et tu fais bien: c'est une technique complexe, mais puissante, et elle dépasse largement le seul cadre de python. Le langage Perl, en particulier l'utilise beaucoup sous linux (peut-être même un peu trop ), mais on le retrouve aussi dans tous les autres langages courants: ça vaut le coup d'investir un peu de temps pour comprendre comment ça marche.

    Enfin, en ce qui me concerne, je privilégie la lisibilité à la rapidité. C'est déjà assez compliqué comme ça. Si en plus une semaine plus tard on ne comprend plus ce qu'on a fait, c'est un sacré défaut...

    Tyrtamos

  6. #6
    Membre Expert
    Profil pro
    Inscrit en
    Janvier 2007
    Messages
    1 418
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Janvier 2007
    Messages : 1 418
    Par défaut
    On ne peut certes pas faire plus court que r"_|\^{" mais c'est en restant dans le cadre de la condition énoncée par rambc, et donc en continuant de détecter des motifs comme
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    '-{{{{{'      '-----{'      '^^^{{{{{' 
    S'il faut limiter strictement l'ensemble des motifs détectés aux 5 en question, il faudra nécessairement faire plus long.



    --------------------

    Enfin, en ce qui me concerne, je privilégie la lisibilité à la rapidité. C'est déjà assez compliqué comme ça. Si en plus une semaine plus tard on ne comprend plus ce qu'on a fait, c'est un sacré défaut...
    Comme le principe de l'informatique c'est tout de même de permettre de simplifier considérablement des taches longues et/ou ardues à un degré tel qu'elles deviennent possibles, je pense qu'il est d'autant plus intéressant de l'utiliser que les taches envisagées sont compliquées.
    Or comme Python est un langage très lisible, je pense que cette lisibilité intrinsèque permet de s'enfoncer plus loin encore dans la complexité de programmes que si on utilisait un autre langage. Je veux dire qu'il ne faut pas s'effaroucher de voir un code s'éloigner de l'aspect épuré des codes destinés à effectuer des taches simples, sous prétexte de perte de lisibilité alors que c'est justement cette lisibilité qui permet des codes un peu complexes.
    Dans le domaine touffu des regex, Python facilite la lisibilité en fournissant la possibilité d'écrire les regex avec le flag VERBOSE.


    --------------------------


    Ta remarque, tyrtamos, sur l'intérêt de conserver une regex composée de 5 sous-regex m'a fait beaucoup phosphorer et j'aboutis à plusieurs objections.


    La première est encore liée à cette histoire d'imprécision de ce qui est détecté.
    Avec
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    x = re.search(r"_{{|_{|_|\^{{|\^{", lesatoms)
    si search() rencontre en premier le motif '_{' ,
    la RE2 va le détecter, x.group() va être '_{' et on sera sûr que ce ne peut pas être "_{{" puisque RE1 n'a pas fonctionné avant RE2.
    Par contre si search() sort de son chapeau un x.group() valant '_{{', on ne saura pas si c'est parce qu'elle a rencontré '_{{' ou '_{{{{{{{{{{{{{{{'.
    En mettant "_{{|_{|_" dans la regex composée, on améliore la précision par rapport à "_|\^{" , mais ça reste une précision médiocre. Si c'est ce qui est recherché, rien à dire, mais il ne faut pas se laisser illusionner par le panel de 5 sous-RE.
    De plus, il reste que RE3 va détecter '____' , RE2 va détecter '____{' etc.

    Donc pour un objectif demandant plus de précision, il faudra de toutes façons faire appel à des RE complétées telles que
    (?<![_{^])-}}(?![_{^])
    pour éviter les signes _{et^ avant et après le motif limité à '_{{'.
    Ce qui pour l'ensemble de la regex composée donnerait:
    r"(?<![_{^])-}}(?![_{^])|(?<![_{^])-}(?![_{^])|(?<![_{^])-(?![_{^])|(?<![_{^])^}(?![_{^])|(?<![_{^])^}}(?![_{^])"
    Pour 5 sous-RE, ça va encore, mais on voit bien que ça devient vite impraticable et illisible si ce nombre augmente.


    La deuxième objection est que Python arrive parfaitement à dire quel est le motif rencontré par search() sans avoir à détailler des sous-RE, comme le montre le programme suivant:

    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
    import re
     
    bla = re.compile(r"_{?{?")
     
    ch = 'jg_hghg ^_{jhg'
    x = re.search(bla,ch)
    print 'x.group(), x.start(), x.end() =',x.group(), x.start(), x.end()
     
    ch = 'jg_{hghg ^_{jhg'
    x = re.search(bla,ch)
    print 'x.group(), x.start(), x.end() =',x.group(), x.start(), x.end()
     
    ch = 'jg_{{hghg ^_{jhg'
    x = re.search(bla,ch)
    print 'x.group(), x.start(), x.end() =',x.group(), x.start(), x.end()
    qui sort
    x.group(), x.start(), x.end() = _ 2 3
    x.group(), x.start(), x.end() = _{ 2 4
    x.group(), x.start(), x.end() = _{{ 2 5



    La troisième objection est qu'on peut vouloir trouver dans une chaine toutes les occurences de chacun des motifs d'un ensemble de motifs différents alors que search() s'arrête de chercher à la première occurence de motif rencontrée.
    Dans ce cas, on est bien obligé de se débrouiller,
    avec autre chose que search() d'une part,
    et surtout en n'ayant pas à écrire explicitement tous les motifs virtuellement envisagés par combinaison de caractères ou groupes de base, le nombre de motifs résultant pouvant très vite devenir rédhibitoire.

    Donc, j'ai cherché, et il m'a fallu garder le couteau entre les dents parce que j'ai eu du mal à piger certains trucs.

    Mais voilà, je suis arrivé à un joli petit programme de démonstration qui cherche 5 sortes de motifs dans une chaine.

    Normalement, en taille 10 de police, les tabulations que j'ai mises doivent donnner une présentation des résultats en colonnes bien alignées.

    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
    import re
     
    bla = re.compile(r"""
       (?<![_{^])   (_)     (?![_{^])   # detecte un underscore solitaire ni précédé ni suivi de ^ ou {
    |  (?<![_{^]) ([_^]{{?) (?![{_^])   # detecte les cas [_^]{{? non précédé de [_{^] et non suivi de [
    |  (?<![_{^])   (;\d)   (?![_{^])   # detecte ;0  ;1  ;2  ;3  etc
    |               (;7X)               # detecte ;7X
    |            (a(y)?:5)              # detecte ay:5 et a:5
    # NB: avec VERBOSE, les blancs qui ne sont pas entre crochets ne participent pas a la definition des regex
    """,re.VERBOSE)
     
    ch = 'jg_{hgay:5yas_{{TTYhg ^{k_hkj__{jfa:5ygh_{{hs;6gf_{{{{gfgf^_{hg^^jfg;7Xzez^{_{{hgf^{{kjh'
     
    # mob = MatchObject
    print '\nliste des matchings et renseignements associés :'
    print '\tgroups()\t\t\tgroup()\tgroup(0)  group(1)     group(2)  span()'
    for mob in re.finditer(bla,ch):
        print mob.groups(),' \t',mob.group(),' \t ',mob.group(0),'\t  ',mob.group(1),'   \t',mob.group(2),'\t',mob.span()
    re.finditer(bla,ch) renvoie un itérateur qui fournit tous les MatchObjects obtenus dans l'exploration de la chaine ch avec la regex bla.
    Sur chaque mob = MatchObject fourni par finditer() , on peut faire agir groups() pour afficher la façon dont a réagi chaque groupe présent dans la regex quand celle-ci détecte une sous-chaine qui matche.


    J'ai remarqué que contrairement à search() où ce n'est pas nécessaire, il faut que dans la regex chaque motif décrit par une sous-RE soit entre parenthèses pour que la trace de la détection du motif correspondant soit enregistré par groups(). Sans cela, le matching du motif en sous-chaine et du motif dans la sous-RE a bien lieu quand même, mais la sous-chaine détectée n'est pas enregistrée dans le tuple groups() .

    En réalité, ceci provient du fait que
    - d'une part
    «Groups are numbered starting with 0. Group 0 is always present; it's the whole RE, so MatchObject methods all have group 0 as their default argument. »
    http://www.amk.ca/python/howto/regex/
    Je ne sais pas pourquoi il appelle ça "whole RE", il s'agit en fait de la sous-chaine détectée

    - et d'autre part
    «groups( [default])
    Return a tuple containing all the subgroups of the match, from 1 up to however many groups are in the pattern.»

    http://www.python.org/doc/2.5/lib/match-objects.html#l2h-426

    Donc, quel que soit ce qu'on trouve dans groups() en fonction de la façon dont on a écrit la regex, la détection d'un motif a toujours lieu et il est enregistré dans group(0) auquel renvoie le défaut d'argument dans group() .
    Voilà pourquoi quand on utilise search() , x.group() donne le motif détecté, qui est unique et se trouve dans x.group(0) , même s'il n'y a pas de parenthèses autour du motif dans la regex.
    Le code suivant met ceci en évidence:
    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
    import re
     
    bla = re.compile(r"""
       (?<![_{^])   (_)     (?![_{^])   # detecte un underscore solitaire ni précédé ni suivi de ^ ou {
    |  (?<![_{^]) [_^]{{? (?![{_^])   # detecte les cas [_^]{{? non précédé de [_{^] et non suivi de [
    |  (?<![_{^])   ;\d   (?![_{^])   # detecte ;0  ;1  ;2  ;3  etc
    |                ;7X              # detecte ;7X
    |            a(y)?:5              # detecte ay:5 et a:5
    # NB: avec VERBOSE, les blancs qui ne sont pas entre crochets ne participent pas a la definition des regex
    """,re.VERBOSE)
     
    ch = 'jg_{hgay:5yas_{{TTYhg ^{k_hkj__{jfa:5ygh_{{hs;6gf_{{{{gfgf^_{hg^^jfg;7Xzez^{_{{hgf^{{kjh'
     
    x = re.search(bla,ch)
    print 'x.groups(),x.group(),x.start(),x.end() =',x.groups(),x.group(), x.start(), x.end()
     
    print
     
    # mob = MatchObject
    print '\nliste des matchings et renseignements associés :'
    print '  groups()\tgroup()\tgroup(0)  group(1)     group(2)  span()'
    for mob in re.finditer(bla,ch):
        print mob.groups(),'  \t',mob.group(),' \t ',mob.group(0),'\t  ',mob.group(1),'   \t',mob.group(2),'\t',mob.span()


    Les matchings peuvent être obtenus directement par re.findall() sans passer par les MatchObjects donnés par l'itérateur re.finditer():
    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
    import re
     
    bla = re.compile(r"""
       (?<![_{^])   (_)     (?![_{^])   # detecte un underscore solitaire ni précédé ni suivi de ^ ou {
    |  (?<![_{^]) ([_^]{{?) (?![{_^])   # detecte les cas [_^]{{? non précédé de [_{^] et non suivi de [
    |  (?<![_{^])   (;\d)   (?![_{^])   # detecte ;0  ;1  ;2  ;3  etc
    |               (;7X)               # detecte ;7X
    |            (a(y)?:5)              # detecte ay:5 et a:5
    # NB: avec VERBOSE, les blancs qui ne sont pas entre crochets ne participent pas a la definition des regex
    """,re.VERBOSE)
     
    ch = 'jg_{hgay:5yas_{{TTYhg ^{k_hkj__{jfa:5ygh_{{hs;6gf_{{{{gfgf^_{hg^^jfg;7Xzez^{_{{hgf^{{kjh'
     
    # mob = MatchObject
    print '\nliste des matchings et renseignements associés :'
    print '\tgroups()\t\t\tgroup()\tgroup(0)  group(1)     group(2)  span()'
    for mob in re.finditer(bla,ch):
        print mob.groups(),' \t',mob.group(),' \t ',mob.group(0),'\t  ',mob.group(1),'   \t',mob.group(2),'\t',mob.span()
     
    print '\nliste des matchings et des span() :'
    for u in re.findall(bla,ch):
        print u
    Pour obtenir des '' dans les matchings au lieu de 'None' en passant par re.finditer(), il faut écrire

    Si l'on veut obtenir seulement les sous-chaines qui matchent en utilisant re.findall() , il faut faire:
    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
    import re
     
    bla = re.compile(r"""
       (?<![_{^])   (_)     (?![_{^])   # detecte un underscore solitaire ni précédé ni suivi de ^ ou {
    |  (?<![_{^]) ([_^]{{?) (?![{_^])   # detecte les cas [_^]{{? non précédé de [_{^] et non suivi de [
    |  (?<![_{^])   (;\d)   (?![_{^])   # detecte ;0  ;1  ;2  ;3  etc
    |               (;7X)               # detecte ;7X
    |            (a(y)?:5)              # detecte ay:5 et a:5
    # NB: avec VERBOSE, les blancs qui ne sont pas entre crochets ne participent pas a la definition des regex
    """,re.VERBOSE)
     
    ch = 'jg_{hgay:5yas_{{TTYhg ^{k_hkj__{jfa:5ygh_{{hs;6gf_{{{{gfgf^_{hg^^jfg;7Xzez^{_{{hgf^{{kjh'
     
    # mob = MatchObject
    print '\nliste des matchings et renseignements associés :'
    print '\tgroups()\t\tgroup()\tgroup(0)  group(1)     group(2)  span()'
    for mob in re.finditer(bla,ch):
        print mob.groups(''),' \t',mob.group(),' \t ',mob.group(0),'\t  ',mob.group(1),'   \t',mob.group(2),'\t',mob.span()
     
    print '\nliste des matches par re.findall() :'
    limatches = [ ma for matching in re.findall(bla,ch) for ma in matching if ma ]
    for ma in limatches:
        print ma
    C'est un peu plus compliqué, et moins lisible: je préfère passer par le recours à re.finditer() qui est plus efficace et plus lisible, et permet d'avoir facilement d'autres renseignements en plus.
    Malgré cela, les deux méthodes ne nécessitent pas de détailler de façon exhaustive toutes les sous-RE à la main.

Discussions similaires

  1. Simplifier un test avec arguments
    Par dodo91 dans le forum Langage
    Réponses: 3
    Dernier message: 10/06/2009, 16h07
  2. Script test de deux chaine avec if
    Par kacedda dans le forum Linux
    Réponses: 6
    Dernier message: 02/05/2003, 15h38
  3. [XMLRAD] test de nullité
    Par Pm dans le forum XMLRAD
    Réponses: 5
    Dernier message: 29/11/2002, 10h57
  4. test collisions
    Par tatakinawa dans le forum OpenGL
    Réponses: 5
    Dernier message: 08/06/2002, 06h03

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