Partage
  • Partager sur Facebook
  • Partager sur Twitter

Expressions régulières

Trouver tous les nombres entiers "naturels"

    4 avril 2011 à 2:33:03

    Bonjour,


    Je commence à m'entraîner sur les expressions régulières, cf. sur le tuto officiel cette leçon en lien pour une première approche.


    Problème



    Déterminer tous les nombres entiers "vrais" apparaissant dans un texte comme le suivant :

    Alpha31, le dimanche 10 avril 2011, 5 croissants pour 3,60 euros.


    Ici, le programme doit trouver les entiers 10, 2011 et 5 mais ne doit pas donner 31 ni 3,60
    (mais ça peut se discuter ;) ).


    Il est possible que mon exemple ne recouvre pas tous les cas courants mais je pense que vous voyez l'idée. Il resterait des cas à trancher comme le formatage de grands nombres, genre un code postal qui s'écrit avec une espace (75 000) ou les ordinaux (genre : 25ème). Il peut y avoir des variantes : chaînes régulières verbeuses, prise en compte de caractères non-ascii comme dans 25°C, donner la position (par un indice) de chacun des motifs trouvés, etc.


    Je ne sais pas si sur ce forum il y a des pratiquants des expressions régulières. Moi, je découvre, j'ai lu la doc officielle ainsi que le howto, j'ai pas trouvé ça super facile (enfin, j'ai surtout pas trouvé les explications toujours claires, au moins sur les trucs non triviaux).
    • Partager sur Facebook
    • Partager sur Twitter
    Anonyme
      4 avril 2011 à 7:44:11

      Citation

      Je ne sais pas si sur ce forum il y a des pratiquants des expressions régulières. Moi, je découvre, j'ai lu la doc officielle ainsi que le howto, j'ai pas trouvé ça super facile (enfin, j'ai surtout pas trouvé les explications toujours claires, au moins sur les trucs non triviaux).



      Il m'a fallu beaucoup de temps pour comprendre juste la base, c'est à dire la syntaxe. Encore maintenant il se peut que je les utilise, mais je ne le maîtrise pas totalement. Certes souvent je trouve le résultat, mais je suis pratiquement toujours sûr qu'il y a plus court ou plus efficace que mon pattern.

      En ce qui concerne ton problème la méthode que j'utiliserais serait assez simplisme, voir peut-être trop, mais j'utiliserais le pattern \s\d+\s avec la méthode findall du module re, car on doit trouver plusieurs nombres dans le texte de ce type.

      \s représentant un espace
      \d+ représentant un nombre ou plus compris entre 0 et 9

      • Partager sur Facebook
      • Partager sur Twitter
        4 avril 2011 à 8:36:00

        Citation : fred1599


        j'utiliserais le pattern \s\d+\s avec la méthode findall du module re, car on doit trouver plusieurs nombres dans le texte de ce type.

        \s représentant un espace
        \d+ représentant un nombre ou plus compris entre 0 et 9



        Euh oui, tu sais comment on dit : in code we trust ;)
        • Partager sur Facebook
        • Partager sur Twitter
        Anonyme
          4 avril 2011 à 9:17:50

          >>> import re
          >>> data = "Alpha31, le dimanche 10 avril 2011, 5 croissants pour 3,60 euros."
          >>> nb = re.findall("\s\d+,*?", data)
          


          Le problème c'est que je ne vois pas comment virer le 3 ou plutôt comment distinguer un nombre décimal d'une ponctuation.

          Dans ce cas, le texte est mal formaté je pense, car on devrait faire la distinction.
          • Partager sur Facebook
          • Partager sur Twitter
            4 avril 2011 à 10:37:32

            Bonjour,

            Citation : fred1599

            Le problème c'est que je ne vois pas comment virer le 3 ou plutôt comment distinguer un nombre décimal d'une ponctuation.
            Dans ce cas, le texte est mal formaté je pense, car on devrait faire la distinction.



            Il me semble qu'une virgule qui marque une ponctuation est toujours suivie d'un espace, ce qui n'est pas le cas dans un décimal.

            Ainsi en adaptant ton pattern ça semble fonctionner :

            import re
            
            data = "Alpha31, le dimanche 10 avril 2011, 5 croissants pour 3,60 euros."
            
            print re.findall("\s\d+(?!,\d+)", data)
            

            $ python /tmp/p.py
            [' 10', ' 2011', ' 5']


            En utilisant :

            Citation : doc

            (?!...)
            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'.

            • Partager sur Facebook
            • Partager sur Twitter
              4 avril 2011 à 11:57:15

              Citation : Grinwik



              Ainsi en adaptant ton pattern ça semble fonctionner :

              import re
              
              data = "Alpha31, le dimanche 10 avril 2011, 5 croissants pour 3,60 euros."
              
              print re.findall("\s\d+(?!,\d+)", data)
              


              $ python /tmp/p.py
              [' 10', ' 2011', ' 5']


              On capture des espaces qu'on ne demande pas de capturer. Mais effectivement, il faut utiliser des "lookahead assertions". Et l'expression régulière pose d'autres problèmes, exemple :


              import re
              
              data = "3 pains 41alpha (49 euros = 69,7 dollars)."
              
              print re.findall("\s\d+(?!,\d+)", data)
              


              [' 41', ' 6']


              Le 3 initial et 49 ne sont pas capturés
              41 est capturé, 6 est capturé, ils ne devraient pas

              • Partager sur Facebook
              • Partager sur Twitter
                4 avril 2011 à 22:09:05

                Je me lance.
                Je dois probablement oublié des cas mais bon, on est là pour ça aussi !
                La regex étant un peu lourde (surtout à cause des parenthèses non capturantes) j'ai divisé en 3 strings.
                start = "(?:^|^|[\(\[\{])"
                end = "(?=$|\s|[\)\]\}]|(?:e|è)(?:r|me)?[\s,.]|(?:[.,] ))"
                regex = start + "([0-9]+)" + end
                s = "3 pains 41alpha (49 euros = 69,7 dollars), 2ème fois."
                s2 = "Alpha31, le dimanche 10 avril 2011, 5 croissants pour 3,60 euros."
                print(re.findall(regex, s))
                print(re.findall(regex, s2))
                

                ['3', '49', '2']
                ['10', '2011', '5']

                Bon pour expliquer un peu parce-que la regex est affreuse à lire :
                pour le caractère précédent, je cherche soit le début de chaine, soit un caractère vide (\s), soit une parenthèse (ou un crochet) ouvrant ("{[(").
                Pour le groupe à capturer, je pense que c'est évident.
                Pour la fin, je cherche en lookahead soit la fin de la chaine, soit une parenthèse (ou crochet) fermant, soit un espace blanc, soit "ème" (ou encore "è", "e", "er" et "eme") suivi d'une virgule, d'un point ou d'un caractère blanc, soit une virgule ou un point suivi d'un espace.
                Pour les défauts dont je suis conscient, je ne me suis pas occupé des cas du genre 20°C encore. Ensuite, la regex accepterait quelque chose du style "31èr", mais n'ayant pas envie de rendre ça plus lourd que ça ne l'est déjà inutilement on va fermer les yeux.
                Je suis preneur de tout conseil, la lourdeur de ma regex ne me plaisant absolument pas.
                • Partager sur Facebook
                • Partager sur Twitter

                Expressions régulières

                × Après avoir cliqué sur "Répondre" vous serez invité à vous connecter pour que votre message soit publié.
                × Attention, ce sujet est très ancien. Le déterrer n'est pas forcément approprié. Nous te conseillons de créer un nouveau sujet pour poser ta question.
                • Editeur
                • Markdown