7.4. Utilisation de la syntaxe {n,m}

Dans la section précédente, nous avons vu un motif dans lequel le même caractère pouvait être répété jusqu’à trois fois. Il y a une autre manière d’exprimer cela dans les expressions régulière, que certaines personnes trouvent plus lisible. D’abord, revenons sur la méthode que nous avons utilisé dans l’exemple précédent.

Exemple 7.5. L’ancienne méthode : chaque caractère est optionnel

>>> import re
>>> pattern = '^M?M?M?$'
>>> re.search(pattern, 'M')    1
<_sre.SRE_Match object at 0x008EE090>
>>> pattern = '^M?M?M?$'
>>> re.search(pattern, 'MM')   2
<_sre.SRE_Match object at 0x008EEB48>
>>> pattern = '^M?M?M?$'
>>> re.search(pattern, 'MMM')  3
<_sre.SRE_Match object at 0x008EE090>
>>> re.search(pattern, 'MMMM') 4
>>> 
1 Cette chaîne est reconnue : le motif reconnaît le début de la chaîne, puis le premier M optionnel, mais pas de second ni de troisième M (ce qui est correct puisqu'ils sont optionnels), puis la fin de la chaîne.
2 Le motif reconnaît le début de la chaîne, puis le premier et le second M optionnels, mais pas de troisième M (ce qui est correct puisqu'il est optionnel), puis la fin de la chaîne.
3 Le motif reconnaît le début de la chaîne, puis les trois M optionnels, puis la fin de la chaîne.
4 Le motif reconnaît le début de la chaîne, puis les trois M optionnels, mais pas la fin de la chaîne (puisqu'il reste un M), la chaîne n'est donc pas reconnue et None est retourné.

Exemple 7.6. La nouvelle méthode : de n à m

>>> pattern = '^M{0,3}$'       1
>>> re.search(pattern, 'M')    2
<_sre.SRE_Match object at 0x008EEB48>
>>> re.search(pattern, 'MM')   3
<_sre.SRE_Match object at 0x008EE090>
>>> re.search(pattern, 'MMM')  4
<_sre.SRE_Match object at 0x008EEDA8>
>>> re.search(pattern, 'MMMM') 5
>>> 
1 Ce motifie signifie : «reconnaître le début de la chaîne, puis de zéro à trois caractères M, puis la fin de la chaîne.» Le 0 et le 3 peuvent être n’importe quel nombre, si nous voulons reconnaître au moins un, mais pas plus de trois caractères M, nous pouvons écrire M{1,3}.
2 Le motif reconnaît le début de la chaîne, puis un M sur trois possibles, puis la fin de la chaîne.
3 Le motif reconnaît le début de la chaîne, puis deux M sur trois possibles, puis la fin de la chaîne.
4 Le motif reconnaît le début de la chaîne, puis trois M sur trois possibles, puis la fin de la chaîne.
5 Le motif reconnaît le début de la chaîne, puis trois M sur trois possibles, puis ne reconnaît pas la fin de la chaîne. L’expression régulière permet jusqu'à trois caractères M avant la fin de la chaîne, mais il y en a quatre, donc la chaîne n'est pas reconnue et None est retourné.
NOTE
Il n’y a aucun moyen de déterminer par un programme que deux expressions régulières sont équivalentes. Le mieux que vous puissiez faire est d’écrire de nombreux cas de test pour vérifier que leur comportements sont identiques pour les entrées pertinentes. Nous discuterons plus en détail l’écriture de cas de tests plus loin dans le livre.

7.4.1. Rechercher les dizaines et les unités

Maintenant, nous allons étendre l’expression régulière pour prendre en compte les dizaines et les unités. L’exemple suivant montre la recherche des dizaines.

Exemple 7.7. Rechercher les dizaines

>>> pattern = '^M?M?M?M?(CM|CD|D?C?C?C?)(XC|XL|L?X?X?X?)$'
>>> re.search(pattern, 'MCMXL')    1
<_sre.SRE_Match object at 0x008EEB48>
>>> re.search(pattern, 'MCML')     2
<_sre.SRE_Match object at 0x008EEB48>
>>> re.search(pattern, 'MCMLX')    3
<_sre.SRE_Match object at 0x008EEB48>
>>> re.search(pattern, 'MCMLXXX')  4
<_sre.SRE_Match object at 0x008EEB48>
>>> re.search(pattern, 'MCMLXXXX') 5
>>> 
1 Le motif reconnaît le début de la chaîne, le premier M optionnel, puis CM, puis XL, puis la fin de la chaîne. Rappelez-vous que la syntaxe (A|B|C) signifie «reconnaître un seul parmi A, B et C». XL est reconnu, donc XC et L?X?X?X? sont ignorés, puis la find de la chaîne est reconnue. MCML est la représentation en chiffres romains de 1940.
2 Le motif reconnaît le début de la chaîne, le premier M optionnel, puis CM, puis L?X?X?X?. Pour L?X?X?X?, il reconnaît L et saute les trois caractères X optionnels. Il reconnaît ensuite la fin de la chaîne. MCML est la représentation en chiffres romains de 1950.
3 Le motif reconnaît le début de la chaîne, le premier M optionnel, puis CM, puis le L optionnel et le premier X optionnel, saute les trois caractères X optionnels, puis reconnaît la fin de la chaîne. MCMLX est la représentation en chiffres romains de 1960.
4 Le motif reconnaît le début de la chaîne, le premier M optionnel, puis CM, puis le L optionnel et les trois caractères X, puis la fin de la chaîne. MCMLXXX est la représentation en chiffres romains de 1980.
5 Le motif reconnaît le début de la chaîne, le premier M optionnel, puis CM, puis le L optionnel et les trois caractères X, puis ne reconnaît pas la fin de la chaîne puisqu'il y a encore un X non pris en charge. Donc la chaîne n'est pas reconnue et None est retourné. MCMLXXXX n'est pas un représentation en chiffres romains valide.

L’expression pour les unités suit le même motif. Je vous épargne les détails et ne vous montre que le résultat final.

>>> pattern = '^M?M?M?M?(CM|CD|D?C?C?C?)(XC|XL|L?X?X?X?)(IX|IV|V?I?I?I?)$'

A quoi est-ce que ça resemble en utilisant la syntaxe alternative avec {n,m} ? L’exemple suivant montre la nouvelle syntaxe.

Exemple 7.8. Validation des chiffres romains avec {n,m}

>>> pattern = '^M{0,4}(CM|CD|D?C{0,3})(XC|XL|L?X{0,3})(IX|IV|V?I{0,3})$'
>>> re.search(pattern, 'MDLV')             1
<_sre.SRE_Match object at 0x008EEB48>
>>> re.search(pattern, 'MMDCLXVI')         2
<_sre.SRE_Match object at 0x008EEB48>
>>> re.search(pattern, 'MMMMDCCCLXXXVIII') 3
<_sre.SRE_Match object at 0x008EEB48>
>>> re.search(pattern, 'I')                4
<_sre.SRE_Match object at 0x008EEB48>
1 Le motif reconnaît le début de la chaîne, puis un sur un maximum de quatre caractères M, puis D?C{0,3}. Pour cette expression il reconnaît le D optionnel et zéro sur un maximum de trois caractères C. Ensuite il reconnaît L?X{0,3} avec le L optionnel et zéro sur un maximum de trois caractères X. Puis il reconnaît V?I{0,3} avec le V optionnel et zéro sur un maximum de trois caractères I, puis la fin de la chaîne. MDLV est la représentation en chiffres romains de 1555.
2 Le motif reconnaît le début de la chaîne, puis deux sur un maximum de quatre caractères M, puis D?C{0,3} avec un D et un sur un maximum de trois caractères C. Ensuite il reconnaît L?X{0,3} avec un L et un sur un maximum de trois caractères X. Puis il reconnaît V?I{0,3} avec un V and et un sur un maximum de trois caractères I, puis la fin de la chaîne. MMDCLXVI est la représentation en chiffres romains de 2666.
3 Le motif reconnaît le début de la chaîne, puis quatre sur un maximum de quatre caractères M, puis D?C{0,3} avec un D et trois sur un maximum de trois caractères C. Ensuite il reconnaît L?X{0,3} avec un L et trois sur un maximum de trois caractères X. Puis il reconnaît V?I{0,3} avec un V et trois sur un maximum de trois caractères I, puis la fin de la chaîne. MMMMDCCCLXXXVIII est la représentation en chiffres romains de 3888 et c'est le chiffre romain le plus long que vous pouvez écrire sans syntaxe étendue.
4 Regardez bien. Le motif reconnaît le début de la chaîne, puis zéro sur un maximum de quatre M, puis D?C{0,3} en sautant le D optionnel et zéro sur un maximum de trois C. Ensuite il reconnaît L?X{0,3} en sautant le L optionnel et zéro sur un maximum de trois X. Puis il reconnaît V?I{0,3} en sautant le V optionnel et un sur un maximum de trois I, puis la fin de la chaîne.

Si vous avez suivi tout cela et que vous l'avez compris du premier coup, vous vous en sortez mieux que moi au début. Maintenant, imaginez devoir comprendre une expression régulière écrite par quelqu'un d'autre au mileu d'un fonction critique d'un programme de grande taille. Ou imaginez simplement de devoir revenir sur une de vos propres expressions régulières quelques mois plus tard. Je l'ai fait et ce n'est pas une partie de plaisir.

Dans la prochaine section vous explorerez une syntaxe alternative qui rendra possible la maintenance de vos expressions régulières.