Sam & Max » regex http://sametmax.com Du code, du cul Sat, 07 Nov 2015 10:56:13 +0000 en-US hourly 1 http://wordpress.org/?v=4.1 Les expressions rationnelles en Python, parfois overkill 14 http://sametmax.com/les-expressions-rationnelles-en-python-parfois-overkill/ http://sametmax.com/les-expressions-rationnelles-en-python-parfois-overkill/#comments Wed, 07 Aug 2013 11:40:11 +0000 http://sametmax.com/?p=7042 J’adore les regex, et d’ailleurs il faudra que je fasse une série d’articles sur le sujet, un peu comme le guide de la POO.

Mais dans un langage comme Python, il y a de nombreuses solutions à mettre en oeuvre avant d’utiliser les regex.

Pour vérifier si une chaîne est dans une autre, utilisez in :

>>> 'a' in 'chat'
True
>>> 'a' in 'chien'
False
>>> 'a' in 'CHAT'.lower() # ignorer la casse
True

Pour savoir si un chaîne est au début ou à la fin, utilisez startswith() et endswith() :

>>> 'achat'.startswith('a')
True
>>> 'chat'.startswith('a')
False
>>> 'acheta'.endswith('a')
True

Pour savoir si la chaîne est d’un type particulier, utiliser les méthodes is* :

>>> '555'.isdigit()
True
>>> ''.isdigit()
False
>>> '⑦'.isdigit()
True
>>> '444'.isdecimal()
True
>>> '444.55'.isdecimal()
False
>> '⑦'.isdecimal()
False
>>> '879fds'.isalpha()
False
>>> 'fsdqfsqd'.isalpha()
True
>>> 'fsdqfsqd'.islower()
True
>>> 'fsdqFFsqd'.islower()
False
>>> '879fds'.isalnum()
True
>>> '879fds-'.isalnum()
False
>>> ' \t\n'.isspace()
True
>>> ' \t\n fdsfd'.isspace()
False

Si vous voulez manipuler la chaîne pour en extraire une partie, utilisez split() (ou rsplit(), lsplit() pour travailler sur la droite ou la gauche de la chaîne) :

>>> s = """Mais, vous savez, moi je ne crois pas qu'il y ait de bonne ou de mauvaise situation. Moi, si je devais résumer ma vie aujourd'hui avec vous, je dirais que c'est d'abord des rencontres, des gens qui m'ont tendu la main, peut-être à un moment où je ne pouvais pas, où j'étais seul chez moi. Et c'est assez curieux de se dire que les hasards, les rencontres forgent une destinée... Parce que quand on a le goût de la chose, quand on a le goût de la chose bien faite, le beau geste, parfois on ne trouve pas l'interlocuteur en face, je dirais, le miroir qui vous aide à avancer. Alors ce n'est pas mon cas, comme je le disais là, puisque moi au contraire, j'ai pu ; et je dis merci à la vie, je lui dis merci et je chante la vie, je danse la vie... Je ne suis qu'amour ! Et finalement, quand beaucoup de gens aujourd'hui me disent "Mais comment fais-tu pour avoir cette humanité ?", et bien je leur réponds très simplement, je leur dis que c'est ce goût de l'amour, ce goût donc qui m'a poussé aujourd'hui à entreprendre une construction mécanique, mais demain, qui sait, peut-être simplement à me mettre au service de la communauté, à faire le don... le don de soi."""
>>> s.split()
[u'Mais,', u'vous', u'savez,', u'moi', u'je', u'ne', u'crois', u'pas', u"qu'il", u'y', u'ait', u'de', u'bonne', u'ou', u'de', u'mauvaise', u'situation.', u'Moi,', u'si', u'je', u'devais', u'r\xe9sumer', u'ma', u'vie', u"aujourd'hui", u'avec', u'vous,', u'je', u'dirais', u'que', u"c'est", u"d'abord", u'des', u'rencontres,', u'des', u'gens', u'qui', u"m'ont", u'tendu', u'la', u'main,', u'peut-\xeatre', u'\xe0', u'un', u'moment', u'o\xf9', u'je', u'ne', u'pouvais', u'pas,', u'o\xf9', u"j'\xe9tais", u'seul', u'chez', u'moi.', u'Et', u"c'est", u'assez', u'curieux', u'de', u'se', u'dire', u'que', u'les', u'hasards,', u'les', u'rencontres', u'forgent', u'une', u'destin\xe9e...', u'Parce', u'que', u'quand', u'on', u'a', u'le', u'go\xfbt', u'de', u'la', u'chose,', u'quand', u'on', u'a', u'le', u'go\xfbt', u'de', u'la', u'chose', u'bien', u'faite,', u'le', u'beau', u'geste,', u'parfois', u'on', u'ne', u'trouve', u'pas', u"l'interlocuteur", u'en', u'face,', u'je', u'dirais,', u'le', u'miroir', u'qui', u'vous', u'aide', u'\xe0', u'avancer.', u'Alors', u'ce', u"n'est", u'pas', u'mon', u'cas,', u'comme', u'je', u'le', u'disais', u'l\xe0,', u'puisque', u'moi', u'au', u'contraire,', u"j'ai", u'pu', u';', u'et', u'je', u'dis', u'merci', u'\xe0', u'la', u'vie,', u'je', u'lui', u'dis', u'merci', u'et', u'je', u'chante', u'la', u'vie,', u'je', u'danse', u'la', u'vie...', u'Je', u'ne', u'suis', u"qu'amour", u'!', u'Et', u'finalement,', u'quand', u'beaucoup', u'de', u'gens', u"aujourd'hui", u'me', u'disent', u'"Mais', u'comment', u'fais-tu', u'pour', u'avoir', u'cette', u'humanit\xe9', u'?",', u'et', u'bien', u'je', u'leur', u'r\xe9ponds', u'tr\xe8s', u'simplement,', u'je', u'leur', u'dis', u'que', u"c'est", u'ce', u'go\xfbt', u'de', u"l'amour,", u'ce', u'go\xfbt', u'donc', u'qui', u"m'a", u'pouss\xe9', u"aujourd'hui", u'\xe0', u'entreprendre', u'une', u'construction', u'm\xe9canique,', u'mais', u'demain,', u'qui', u'sait,', u'peut-\xeatre', u'simplement', u'\xe0', u'me', u'mettre', u'au', u'service', u'de', u'la', u'communaut\xe9,', u'\xe0', u'faire', u'le', u'don...', u'le', u'don', u'de', u'soi.']
>>> s.split()[0]
u'Mais,'
>>> s.split()[5:7]
[u'ne', u'crois']
>>> s.split(',')
[u'Mais', u' vous savez', u" moi je ne crois pas qu'il y ait de bonne ou de mauvaise situation. Moi", u" si je devais r\xe9sumer ma vie aujourd'hui avec vous", u" je dirais que c'est d'abord des rencontres", u" des gens qui m'ont tendu la main", u' peut-\xeatre \xe0 un moment o\xf9 je ne pouvais pas', u" o\xf9 j'\xe9tais seul chez moi. Et c'est assez curieux de se dire que les hasards", u' les rencontres forgent une destin\xe9e... Parce que quand on a le go\xfbt de la chose', u' quand on a le go\xfbt de la chose bien faite', u' le beau geste', u" parfois on ne trouve pas l'interlocuteur en face", u' je dirais', u" le miroir qui vous aide \xe0 avancer. Alors ce n'est pas mon cas", u' comme je le disais l\xe0', u' puisque moi au contraire', u" j'ai pu ; et je dis merci \xe0 la vie", u' je lui dis merci et je chante la vie', u" je danse la vie... Je ne suis qu'amour ! Et finalement", u' quand beaucoup de gens aujourd\'hui me disent "Mais comment fais-tu pour avoir cette humanit\xe9 ?"', u' et bien je leur r\xe9ponds tr\xe8s simplement', u" je leur dis que c'est ce go\xfbt de l'amour", u" ce go\xfbt donc qui m'a pouss\xe9 aujourd'hui \xe0 entreprendre une construction m\xe9canique", u' mais demain', u' qui sait', u' peut-\xeatre simplement \xe0 me mettre au service de la communaut\xe9', u' \xe0 faire le don... le don de soi.']
>>> s.split('.')
[u"Mais, vous savez, moi je ne crois pas qu'il y ait de bonne ou de mauvaise situation", u" Moi, si je devais r\xe9sumer ma vie aujourd'hui avec vous, je dirais que c'est d'abord des rencontres, des gens qui m'ont tendu la main, peut-\xeatre \xe0 un moment o\xf9 je ne pouvais pas, o\xf9 j'\xe9tais seul chez moi", u" Et c'est assez curieux de se dire que les hasards, les rencontres forgent une destin\xe9e", u'', u'', u" Parce que quand on a le go\xfbt de la chose, quand on a le go\xfbt de la chose bien faite, le beau geste, parfois on ne trouve pas l'interlocuteur en face, je dirais, le miroir qui vous aide \xe0 avancer", u" Alors ce n'est pas mon cas, comme je le disais l\xe0, puisque moi au contraire, j'ai pu ; et je dis merci \xe0 la vie, je lui dis merci et je chante la vie, je danse la vie", u'', u'', u' Je ne suis qu\'amour ! Et finalement, quand beaucoup de gens aujourd\'hui me disent "Mais comment fais-tu pour avoir cette humanit\xe9 ?", et bien je leur r\xe9ponds tr\xe8s simplement, je leur dis que c\'est ce go\xfbt de l\'amour, ce go\xfbt donc qui m\'a pouss\xe9 aujourd\'hui \xe0 entreprendre une construction m\xe9canique, mais demain, qui sait, peut-\xeatre simplement \xe0 me mettre au service de la communaut\xe9, \xe0 faire le don', u'', u'', u' le don de soi', u'']

Et n’oubliez pas que vous pouvez appeler join() derrière.

Si vous devez altérer la chaîne, utilisez strip() (et rstrip(), lstrip()) ou replace() :

>>> "Les nouilles cuisent au jus de canne".replace('noui', 'coui').replace('cui', 'nui').replace('jus', 'cul').replace('canne', 'jeanne')
u'Les couilles nuisent au cul de jeanne'
>>> "                               .                       ".strip()
u'.'
>>> "===                               .                       ======".strip("= ")
u'.'

En plus, les chaînes sont itérables, indexables et sliceables, donc :

>>> s = """And I will strike down upon thee with great vengeance and furious anger those who attempt to poison and destroy my brothers. And you will know my name is the Lord when I lay my vengeance upon you!"""
>>> s[3:30:3]
u' wltkdnp '
>>> s.split()
[u'And', u'I', u'will', u'strike', u'down', u'upon', u'thee', u'with', u'great', u'vengeance', u'and', u'furious', u'anger', u'those', u'who', u'attempt', u'to', u'poison', u'and', u'destroy', u'my', u'brothers.', u'And', u'you', u'will', u'know', u'my', u'name', u'is', u'the', u'Lord', u'when', u'I', u'lay', u'my', u'vengeance', u'upon', u'you!']
>>> s[0]
u'A'
>>> ''.join([(l.upper() if i % 2 else l) for i, l in enumerate(s)])
u'ANd I wIlL StRiKe dOwN UpOn tHeE WiTh gReAt vEnGeAnCe aNd fUrIoUs aNgEr tHoSe wHo aTtEmPt tO PoIsOn aNd dEsTrOy mY BrOtHeRs. AnD YoU WiLl kNoW My nAmE Is tHe LOrD WhEn I lAy mY VeNgEaNcE UpOn yOu!'

Bref, avant de sortir le bazooka, souvenez-vous que vous avez un arsenal déjà très approprié pour traiter les strings, dont les perfs seront en plus probablement meilleures.

]]>
http://sametmax.com/les-expressions-rationnelles-en-python-parfois-overkill/feed/ 14
Comment parser du HTML avec des Regex ? 26 http://sametmax.com/comment-parser-de-html-avec-des-regex/ http://sametmax.com/comment-parser-de-html-avec-des-regex/#comments Wed, 05 Jun 2013 18:24:09 +0000 http://sametmax.com/?p=216 Il y a longtemps que je voulais traduire cette réponse de StackOverflow, qui donne une bonne fois pour toute la réponse à cette question souvent posée.

Voici (le formatage exotique a été conservé au maximum):

Vous ne pouvez pas analyser [X]HTML avec des regex. Parce qu’HTML ne peut être analysé par des regex. Les regex sont un outil qui ne peut être utilisé pour analyser correctement du HTML. Comme je l’ai répondu dans des question HTML-et-regex ici de nombreuses fois auparavant, l’utilisation de regex ne permet pas de consommer du HTML. Les expressions rationnelles sont un outil qui n’est pas suffisamment sophistiqué pour comprendre les constructions employées par HTML. HTML n’est pas une langage rationnel, et de fait ne peut être parsé par des expressions rationnelles. Les requêtes par regex ne sont pas équipées pour diviser du HTML en parties qui ont du sens. Tant de fois, mais je ne m’en lasse pas. Même des expressions irrationnelles telles qu’utilisées par Perl en sont pas à la hauteur de la tâche que représente l’analyse de HTML. Vous ne me ferez pas céder. HTML est un langage d’une complexité telle qu’il ne peut être analysé par des expressions rationnelles. Même Chuck Norris ne peut analyser du HTML avec des expressions rationnelles. Chaque fois que vous essayez d’analyser du HTML avec des expressions rationnelles, l’enfant damné pleure du sang de vierge, et des hackers russes pown votre webapp. Analyser du HTML avec des regex invoque des âmes perdues dans le royaume des vivants. HTML et les regex vont ensemble comme l’amour, le mariage et les rituels infanticides. Le <center> ne peut tenir, il est trop tard. La force des regex et du HTML ensemble dans le même espace conceptuel détruira votre esprit comme l’humidité détruit les joints. Si vous analysez du HTML avec des regex, vous vous adonnez à Eux et leurs traditions blasphématoires qui nous condamnent tous à un labeur inhumain pour Celui dont le Nom ne peut être exprimé avec le Plan Multilingue Basique, il arrive. HTML-plus-regex liquéfiera les nerfs des êtres les plus sensibles sous vos yeux, votre psyché dépérissant sous les assauts de l’horreur. Les an̷al̵y͘s̕e͝urs ̸HT͝M͡L basés sur les regex sont le cancer qui tue StackOverflow il est trop tard il est trop tard nous ne pouvons être sauvés la trangession d’un en̵f͝ant amènera les regex à consommer tout tissue vivant (excepté pour le HTML puisqu’il ne le peut, tel que prophétisé précédemment) oh seigneur aide nous comment qui que ce soit peut-il survire à cet fléau utiliser des regex pour analyser du HTML a condamné l’humanité à la terreur de la torture and aux failles de sécurité utiliser les regex comme outil pour travailler du HTML établie un brèche entre ce monde et le royaume terrifiant des entités corrompues (comme les entités SGML, mais plus corrompues) un bref regard vers le monde des analyseurs regex pour HTML transportera instantanément la conscience d’un programmeur dans un monde de cris sans fin, il arrive, la nauséabonde plaie des infections regex dévoreront votre parser HTML, votre application et votre existence pour l’éternité comme l’a fait Visual Basic mais en pire il arrive il arrive ne vous débattez pas i̢l͞ a͘rr̸i҉v̡é,̨ sa radiance damnée détruisant toute illumination, des ͠t̕a̕g͡s ̶H͞TM̵L͞ ̕c̸o͜ùlan͜t̀ de̶ vos͜ ͟y͏eưx te̷l̛s̡ de l͜a̶ ̛d͝ǫu͜lȩur҉ ͝l̡iquid͝ee, la chanson de l’analyse par expressions rationnelles reduit au silence les voix de l’homme mortel depuis la sphère je peux le voir pouvez-vous le voir c’est magnifique l’expurgation de tous les mensonges de l’Homme  TOUT EST PERDU TOUT EST PERDU le poney il arrive i͟l arrive il̸ ̕arr̴i҉ve͢ ̕l’̵ich҉or ̸p̴é͝n͘è͟t̡r͞e tout MON V̴ISA҉ĢE͘͜ M̵̢͟O̶N ͢͏V͏I̧̨S͞AG̶̕͞E̷̡̕ oh mon dieu no Ņ̵̨ON̕ ̢҉̀N̸͡͞OO͏̢͝O̶͠O̷҉N҉͝ ͡N̶͘͏O̧N͟ ́͜͡s̀͟͡t̶͟o̧͝p̧ ̕͘l̴e҉͠s ͟ań̴gl͏ę̀͏s͢͝ ͜͝n̡e͏̡ ́so҉͜nt̵̡ ͢p̧͘a̛s̀ ̸͞r͞é̕͞e̛l̸͏s̨ ̧̀ZA̷Ĺ͝GO̸ ̸E̴͜͝Ş̵̛T̴̸͠ ̨T̨̢Ó͜N͡Y̢ ̷̨̕L̀E̵͠ PONE̕Y͟ I͖̮̝̥̞͟L̴̬͈͍͞ ̞̦A̴҉͕̩͉R͎͉̯̼̹̫͈̹R̩͎̙̥̦I̶̢͓̘̗V̤̳͓͓͇̯̭̯̥͜͢͢E̹̣͚̩̯̙͡.

Avez vous essayé un parser XML à la place ?

]]>
http://sametmax.com/comment-parser-de-html-avec-des-regex/feed/ 26
Grin, le grep Python sous steroïd 11 http://sametmax.com/grin-le-grep-python-sous-steroid/ http://sametmax.com/grin-le-grep-python-sous-steroid/#comments Sat, 25 Aug 2012 16:52:53 +0000 http://sametmax.com/?p=1883 Chercher un mot dans 500 fichiers est un truc que nous, pauvres développeurs, avons besoin de faire assez souvent.

Sublime Text le fait très bien avec Maj + Ctrl + F, mais on est pas toujours dans son éditeur. La recherche dans tout le système type Spotlight est généralement bien large, et la seule solution qui ait la bonne puissance de frappe, tout en affichant plein de détails reste grep. Problème, il faut connaître un max d’options de grep, utiliser egrep pour avoir de bonnes regex, et piper la sortie pour avoir certains résultats.

Le monde de perl a trouvé depuis longtemps une solution à ce problème au travers de ack, et comme les pythonistes, c’est que des gros copieurs, ils ont fait leur version: grin.

C’est sur pypi:

pip install grin

Et ça s’utilise ainsi:

grin test

Et il va vous sortir la liste des fichiers qui contiennent “test”, et dans quel contexte, avec coloration:

Capture d'écran de la sortie du programme grin

Génial quand on est en remote sur un serveur

Grin vient avec quelques goodies en plus:

  • Récursif par défaut.
  • Ignore les dossiers .git, .svn, etc et les fichiers temporaires (~, .tmp, etc) par défaut.
  • Cherche à l’intérieur de certaines archives.
  • Très rapide.
  • Accepte une expression rationnelle Python à la place du mot à chercher !

Par exemple pour chercher uniquement les fichiers qui contiennent le mot “test” ou “tests” à la fin d’une ligne dans un fichier Python, récursivement:

~/Work $ grin tests?$ -I *.py
./archives/client/project/apps/accounts/tests.py:
   65 :     # Used in Login test
./archives/client/project/apps/grants/tests.py:
   16 : # moving settings for testing to settings_test
./archives/client/project/apps/hivtest/tests.py:
  293 :         # print updated_hivtest
./archives/client/project/apps/home/tests.py:
   15 : # moving settings for testing to settings_test
...

Le code source est simple à comprendre, et il est fait de manière modulaire de telle sorte qu’on puisse réutiliser grin comme une lib externe pour chercher des choses avec son propre programme.

]]>
http://sametmax.com/grin-le-grep-python-sous-steroid/feed/ 11
Valider une adresse email avec une regex en Python 2 http://sametmax.com/valider-une-adresse-email-avec-une-regex-en-python/ http://sametmax.com/valider-une-adresse-email-avec-une-regex-en-python/#comments Tue, 06 Mar 2012 23:55:21 +0000 http://sametmax.com/?p=253 la norme qui définit le format des adresses emails est vraiment tordue.]]> Problème de tous les jours, et on a tous essayé de concocter notre solution maison. Mais il se trouve que la norme qui définit le format des adresses emails est vraiment tordue.

Si on est trop rigide, on interdit les cas contre-intuitifs comme le signe ‘+’ dans le destinataire, le domaine “.museum”, l’adresse IP dans l’email ou le sous domaine comportant un seul caractère.

Si on est trop flexible, l’utilisateur va rentrer truc@chose et ne pas s’apercevoir de sa faute de frappe. Il oubliera alors votre site, sans email pour le contacter.

Il existe une version bullet proof de regex qui valide une adresse email, mais c’est un truc de malade.

Au final, on veut juste attraper 99% des cas les plus courrants, un compromis en solidité et praticité. Le code source de Django nous fournit justement une solution équilibrée dans le fichier core/validators.py:

>>> email_re = re.compile(
r"(^[-!#$%&'*+/=?^_`{}|~0-9A-Z]+(\.[-!#$%&'*+/=?^_`{}|~0-9A-Z]+)*"  # dot-atom
r'|^"([\001-\010\013\014\016-\037!#-\[\]-\177]|\\[\001-011\013\014\016-\177])*"' # quoted-string
r')@(?:[A-Z0-9](?:[A-Z0-9-]{0,61}[A-Z0-9])?\.)+[A-Z]{2,6}\.?$', re.IGNORECASE)  # domain
>>> email_re.search('test+12@moi.n.museum')
<_sre.SRE_Match object at 0x1205160>
>>> email_re.search('test@moi')
>>>

Par contre, ça ne gère pas l’adresse IP en tant que nom de domaine, mais c’est le plus souvent suffisant.

]]>
http://sametmax.com/valider-une-adresse-email-avec-une-regex-en-python/feed/ 2