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 :

Expression régulière : findall / match


Sujet :

Python

  1. #1
    Modérateur

    Avatar de Bktero
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Juin 2009
    Messages
    4 493
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 37
    Localisation : France, Loire Atlantique (Pays de la Loire)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Juin 2009
    Messages : 4 493
    Billets dans le blog
    1
    Par défaut Expression régulière : findall / match
    Bonjour,

    Je viens de lire une bonne partie du howto sur les regexp de Python 2.7 et j'ai une question pour vous.

    En lisant un fichier, je dois vérifier que les lignes sont semblables à celle-ci AA;03;64;B5;12;02;02;FE;98;, c'est à dire une alternance de nombres hexadécimaux sur 2 digits, séparés par des ; (point-virgules).

    Pour simplifier, je commence par des nombres décimaux et j'ai fait le code suivant :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    >>> print re.findall( re.compile(r"(\d{2};)+"), "12;34;56;")
    ['56;']
    >>> print re.match( re.compile(r"(\d{2};)+"), "12;34;56;")
    <_sre.SRE_Match object at 0x011D2EA0>
    Je suis OK sur le résultat du match, ma chaîne étant bien de la forme attendue. Je suis en revanche plus étonné du résultat de findall. Je m'attendais à obtenir la chaine entière, voire une combinaison de morceaux de la chaine genre ['56;'], ['34;56;'].

    Plusieurs possibilités :
    1. mon expression régulière ne fait pas ce que je veux
    2. je n'ai pas compris le fonctionnement de findall
    3. autre possibilité ^^



    Je fais appel à votre savoir pour savoir quelle possibilité est la bonne. Merci d'avance

  2. #2
    Expert confirmé
    Avatar de fred1599
    Homme Profil pro
    Lead Dev Python
    Inscrit en
    Juillet 2006
    Messages
    4 062
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Meurthe et Moselle (Lorraine)

    Informations professionnelles :
    Activité : Lead Dev Python
    Secteur : Arts - Culture

    Informations forums :
    Inscription : Juillet 2006
    Messages : 4 062
    Par défaut
    Il faut enlever les parenthèses capturantes

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    re.findall( re.compile(r"\d{2};+"), "12;34;56")
    # ['12;', '34;'] --> a chaque élément trouvé il va l'ajouter dans la liste
    Pour avoir l'équivalent de re.match

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    ''.join(re.findall( re.compile(r"\d{2};+"), "12;34;56"))

  3. #3
    Modérateur

    Avatar de Bktero
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Juin 2009
    Messages
    4 493
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 37
    Localisation : France, Loire Atlantique (Pays de la Loire)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Juin 2009
    Messages : 4 493
    Billets dans le blog
    1
    Par défaut
    Le résultat de match() me convient, voici par exemple un test supplémentaire :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    >>> import re
    >>> m = re.match( re.compile(r"(\d{2};)+"), "12;34;56;")
    >>> print m.group()
    12;34;56;
    Enlever les parenthèses capturantes ne me convient pas. En effet, je veux vérifier que la chaine est bien une répétition de mon motif en parenthèses. Je ne cherche pas à construire avec findall() la chaine obtenue par match() / group(). Le code suivant montre que ce n'est pas la solution à mon problème :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    >>> re.findall( re.compile(r"\d{2};+"), "12;;;;34;;;;;56;;;")
    ['12;;;;', '34;;;;;', '56;;;']
    Ma question est en fait d'expliquer la différence entre les 2 commandes suivantes :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    >>> re.match( re.compile(r"(\d{2};)+"), "12;34;56;").group()
    '12;34;56;'
    >>> re.findall( re.compile(r"(\d{2};)+"), "12;34;56;")
    ['56;']
    Pourquoi n'obtient-on pas le même résultat ?

  4. #4
    Expert confirmé
    Avatar de fred1599
    Homme Profil pro
    Lead Dev Python
    Inscrit en
    Juillet 2006
    Messages
    4 062
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Meurthe et Moselle (Lorraine)

    Informations professionnelles :
    Activité : Lead Dev Python
    Secteur : Arts - Culture

    Informations forums :
    Inscription : Juillet 2006
    Messages : 4 062
    Par défaut
    Pourquoi n'obtient-on pas le même résultat ?
    Tout simplement parce-que c'est deux fonctions font deux choses bien distinctes, il est donc difficile de les comparer.

    Si tu veux comparer match() avec search() ou findall() avec finditer() ça sera beaucoup plus simple.

    findall() retourne toutes (plusieurs fois) les sous-chaines de ta chaîne de caractères correspondant au pattern.

    match() retourne juste l'occurence recherchée et à partir du début de la chaîne de caractères.

    search() quand à lui fait comme match() et aussi à partir de n'importe quelle position dans la chaîne

    Voilà je sais pas si c'est plus clair,

  5. #5
    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
    le motif recherché est celui-ci*:
    En effet, comme le dit fred, findall effectue un search répétitif à partir de la position atteinte lors du dernier search… Inutile donc de préciser qu’il peut y avoir plusieurs fois le même motif.

    Au contraire, + étant glouton (c-à-d qu’il “avale” autant de caractères que possible), le motif "(\d{2};⁠)+" “mangera” à chaque itération autant de groupes "AB;" que possible –*dans ton exemple, tout le "12;34;56;", exactement comme le fait match.

    Seul le dernier nombre est retourné car, lorsqu’il y en a, findall ne renvoie que le contenu des parenthèses capturantes, et pas l’ensemble matché… Et dans le cas d’une parenthèse capturante pouvant être répétée plusieurs fois (comme c’est le cas avec "(\d{2};⁠)+"), seule la dernière occurence est réellement capturée…

    Subtilités des regex, pas forcément évident à saisir du premier coup*!

  6. #6
    Modérateur

    Avatar de Bktero
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Juin 2009
    Messages
    4 493
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 37
    Localisation : France, Loire Atlantique (Pays de la Loire)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Juin 2009
    Messages : 4 493
    Billets dans le blog
    1
    Par défaut
    Tssssss !

    Merci le howto sur les regexp pas super précis :
    Two pattern methods return all of the matches for a pattern. findall() returns a list of matching strings:
    Ce n'est pas tout à fait exact, si on compare à ce que dit la documentation de la classe RE :
    re.findall(pattern, string, flags=0)
    Return all non-overlapping matches of pattern in string, as a list of strings. The string is scanned left-to-right, and matches are returned in the order found.
    Tout est dans le over-lapping. Voir ce code :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    >>> re.findall( re.compile(r"(\d{2};)+"), "12; et plus loin 34;;56;")
    ['12;', '34;', '56;']

    En prenant la documentation de match cette fois :
    re.match(pattern, string, flags=0)
    If zero or more characters at the beginning of string match the regular expression pattern, return a corresponding MatchObject instance.
    Pour exemples :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    >>> re.match( re.compile(r"(\d{2};)+"), "12;34;56; mais ca match() s'en fiche").group()
    '12;34;56;'
    >>> print re.match( re.compile(r"(\d{2};)+"), "et non ! 12;34;56;")
    None
    En espérant que cela vous a été aussi instructif qu'à moi ^^

    Merci de vous être penchés sur mon problème, je marque comme résolu.

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

Discussions similaires

  1. Expression régulière avec matches()
    Par sunwallis dans le forum Langage
    Réponses: 13
    Dernier message: 01/05/2010, 11h50
  2. [REGEX] expression régulière qui match tout les nombres sauf un
    Par neuromencien dans le forum Collection et Stream
    Réponses: 11
    Dernier message: 28/05/2008, 08h21
  3. Réponses: 2
    Dernier message: 10/11/2006, 11h03
  4. Expressions régulières Java (matchs insuffisants)
    Par jemore dans le forum Langage
    Réponses: 4
    Dernier message: 21/06/2006, 19h55
  5. Réponses: 7
    Dernier message: 27/07/2005, 12h41

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