Programmation Python/Regex

Un livre de Wikilivres.
Sauter à la navigation Sauter à la recherche


Les expressions régulières en Python nécessitent d'importer le module natif re[1], ou bien l'installation du module externe regex[2] si besoin des regex Unicode tels que \X.

Syntaxe[modifier | modifier le wikicode]

Expressions rationnelles courantes
Caractère Type Explication
. Point n'importe quel caractère
[...] classe de caractères tous les caractères énumérés dans la classe
[^...] classe complémentée Tous les caractères sauf ceux énumérés
^ circonflexe marque le début de la chaine, la ligne...
$ dollar marque la fin d'une chaine, ligne...
| barre verticale alternative - ou reconnaît l'un ou l'autre
(...) parenthèse utilisée pour limiter la portée d'un masque ou de l'alternative
* astérisque 0, 1 ou plusieurs occurrences
+ le plus 1 ou plusieurs occurrence
? interrogation 0 ou 1 occurrence
Classes de caractères POSIX[3]
Classe Signification
[[:alpha:]] n'importe quelle lettre
[[:digit:]] n'importe quel chiffre
[[:xdigit:]] caractères hexadécimaux
[[:alnum:]] n'importe quelle lettre ou chiffre
[[:space:]] n'importe quel espace blanc
[[:punct:]] n'importe quel signe de ponctuation
[[:lower:]] n'importe quelle lettre en minuscule
[[:upper:]] n'importe quelle lettre capitale
[[:blank:]] espace ou tabulation
[[:graph:]] caractères affichables et imprimables
[[:cntrl:]] caractères d'échappement
[[:print:]] caractères imprimables exceptés ceux de contrôle
Expressions rationnelles Unicode[4]
Expression Signification
\A Début de chaine
\b Caractère de début ou fin de mot
\d Chiffre
\D Non chiffre
\s Caractères espace
\S Non caractères espace
\W Caractère qui n'est pas lettre, chiffre ou underscore
\w Lettre, chiffre ou underscore
\X Caractère Unicode
\z Fin de chaine

Débogueur : https://regex101.com/

Remarques :

  • Les caractères de débuts et fin de chaines ne fonctionnent pas dans [].
  • Les opérateurs * et + sont toujours avides, pour qu'ils laissent la priorité il faut leur apposer un ? à leur suite.

De plus, Python propose :

  • la négation (?!). Attention : il faut toujours la faire suivre de .*. Exemple pour avoir toutes les balises HTML sauf "body" : <(?!body).*>.
  • Ignorer le groupe de capture dans la numérotation (?:)
  • negative lookbehind (?<!)
  • positive lookbehind (?<=)
  • negative lookahead (?!)
  • positive lookahead (?=)

Types[modifier | modifier le wikicode]

Les types de caractères sont représentés par :

  • \n : fin de ligne.
  • \b : fin de mot.
  • \w : caractère alphanumérique.
  • \d : caractère numérique.
  • \s : espace.

Certains ont leurs opposés en capitale :

  • \W : caractère non-alphanumérique.
  • \D : caractère non-numérique.
  • \S : non-espace.

Recherche[modifier | modifier le wikicode]

  • compile() renvoie None si l'expression rationnelle n'est pas trouvée dans la chaine.
  • search() renvoie la position des chaines recherchées.
#!/usr/bin/env python
import re
chaine = "Test regex Python pour Wikibooks francophone."
if re.compile('Wikibooks').search(chaine):
	print "Position du mot Wikibooks : "
	print re.search(u'Wikibooks', chaine).start()
        # Affiche "23"
        print re.search(u'Wikibooks', chaine).end()
        # Affiche "32"

Pour voir le pattern compilé : re.compile('Wikibooks').pattern

  • findall() trouve toutes les correspondances dans un tableau.
  • finditer() trouve toutes les correspondances dans un itérateur.
#!/usr/bin/env python
# Affiche tous les mots qui commencent par "Wiki"
import re
chaine = "Wikilivre regex Python pour Wikibooks francophone."
print (re.findall(r"Wiki\w+", chaine))
# Affiche ['Wikilivre', 'Wikibooks']


Les parenthèses imbriquées permettent d'indiquer des mots facultatifs au sein d'un groupe de capture. Ex :

#!/usr/bin/env python
# Trouve à un mot prêt
import re
chaine = "Wikilivre regex Python pour Wikibooks francophone."

regex = ur'(Python pour Wikibooks)'
print re.search(regex, chaine).start() # 16

regex = ur'(Python (pour )*Wikibooks)'
print re.search(regex, chaine).start() # 16

regex = ur'(Python pour (les )*Wikibooks)'
print re.search(regex, chaine).start() # 16

group()[modifier | modifier le wikicode]

Pour accéder aux résultats des groupes de capture, utiliser group() en partant de 1 (0 étant le match du pattern entier) :

#!/usr/bin/env python
import re
chaine = "Wikilivre regex Python pour Wikibooks francophone."
s = re.search(ur'(Wiki[a-z]*).*(Wiki[a-z]*)', chaine)
if s:
    print s.group(0)
    # Affiche 'Wikilivre regex Python pour Wikibooks'
    print s.group(1)
    # Affiche 'Wikilivre'
    print s.group(2)
    # Affiche 'Wikibooks'

Flags[modifier | modifier le wikicode]

Le comportement de certaines expressions peut être reconfiguré en ajoutant un "flag" en paramètre des méthodes[5].

re.IGNORECASE[modifier | modifier le wikicode]

Ignore la casse. Ainsi dans l'exemple précédent nous pouvions aussi faire :

 s = re.search(ur'(wiki[a-z]*).*(wiki[a-z]*)', chaine, re.IGNORECASE)

re.MULTILINE[modifier | modifier le wikicode]

Par défaut, les caractères "^" et "$" désignent le début et la fin de tout le texte. Or, en mode multiligne, un "^" en début de re.search() considérera le début de chaque ligne, et "$" leurs fins.

Pour partir uniquement du début de la chaine globale, il faut alors ne plus utiliser "re.search()" mais "re.match()"[6].

re.DOTALL[modifier | modifier le wikicode]

Par défaut, .* et .+ s'arrêtent aux retours chariot (\n). Pour qu'ils englobent ces retours à la ligne, il faut appeler re.DOTALL. Exemple :

 if re.search(regex, text, re.MULTILINE| re.DOTALL):

Remplacement[modifier | modifier le wikicode]

#!/usr/bin/env python
# Remplace tous les espaces par des underscores
import re
chaine = "Test regex Python pour Wikibooks francophone."
chaineTriee = re.sub(r' ', "_", chaine)
print chaineTriee
# Affiche "Test_regex_Python_pour_Wikibooks_francophone."

Pour remplacer certains éléments en conservant ceux placés entre parenthèses, il faut les désigner par \1, \2, \3...

#!/usr/bin/env python
# Ajoute des guillemets à tous les mots suivent "livre"
import re
chaine = "Test regex Python pour le livre Python de Wikibooks francophone."
chaineTriee = re.sub(r'(.*)livre (\w+)(.*)', r'\1livre "\2"\3', chaine)
print chaineTriee
# Affiche "Test regex Python pour le livre "Python" de Wikibooks francophone."

Remarque : si les paramètres (\1, \2...) sont remplacés par le symbole �, vérifier que la chaine regex est bien encodée avec r.

Attention !
link={{{link}}}

Les différents contenus d'un même groupe de capture sont remplacés par le premier \1. Pour éviter cela, il faut les traiter un par un avec "finditer()".

Exemple : remplacement de la balise "font color=" par "span style=font-size:".

    text = ur'<font color=green>Vert</font> / <font color=red>rouge</font>'
    regex = ur'<font color=([^>]*)>'
    pattern = re.compile(regex, re.UNICODE)
    for match in pattern.finditer(text):
        print u'Remplacement de ' + match.group(0) + u' par <span style="font-color:' + match.group(1) + u'">'
        text = text.replace(match.group(0), u'<span style="font-color:' + match.group(1) + u'">')
        text = text.replace('</font>', u'</span>')
    raw_input(text)

Exemples de formules[modifier | modifier le wikicode]

  • Récupérer le premier modèle 1 wiki non imbriquée dans un autre modèle :
page = u'{{Modèle2|Paramètre2, {{Modèle1|Paramètre3}} }}, {{Modèle1|Paramètre4}}'
regex = ur'({{(.*?)}}|.)*[^}]*'
raw_input(re.sub(regex, ur'\2', page).encode(config.console_encoding, 'replace'))

Pour indiquer un nombre précis d'occurrences, utiliser "{nombre}". Ex :

#!/usr/bin/env python
import re
chaine = 'w.1, ww.2, www.3, wwww.4'
print re.sub(ur' w{3}\.', ' http://www.', chaine)
w.1, ww.2, http://www.3, wwww.4

Idem pour une plage de nombres : {min,max}.

Attention !
link={{{link}}}

Quand on injecte une variable dans un pattern, il faut échapper ses caractères interprétables avec re.escape().

Références[modifier | modifier le wikicode]