7.3. Exemple : chiffres romains

Vous avez certainement déjà vu des chiffres romains, par exemple dans Astérix[2]

En chiffres romains, il y a sept caractères qui sont répétés et combinés de différentes manières pour représenter des nombres.

Voici les règles générales pour construire des nombres romains :

7.3.1. Rechercher les milliers

Qu’est-ce qui serait nécessaire pour vérifier qu’une chaîne de caractères quelconque constitue des chiffres romains valides ? Nous allons opérer caractère par caractère. Puisque les chiffres romains sont toujours écrits du plus grand vers le plus petit, nous allons commencer par les plus grands : les milliers. Pour les nombres supérieurs à 1000, les milliers sont représentés par une série de caractères M.

Exemple 7.3. Rechercher les milliers

>>> import re
>>> pattern = '^M?M?M?$'       1
>>> re.search(pattern, 'M')    2
<SRE_Match object at 0106FB58>
>>> re.search(pattern, 'MM')   3
<SRE_Match object at 0106C290>
>>> re.search(pattern, 'MMM')  4
<SRE_Match object at 0106AA38>
>>> re.search(pattern, 'MMMM') 5
>>> re.search(pattern, '')     6
<SRE_Match object at 0106F4A8>
1 Ce motif a trois parties :
  • ^ - reconnaît ce qui suit uniquement en début de chaîne. Si ce n’était pas spécfié, le motif reconnaîtrait les M où qu’ils soient, ce qui n’est pas ce que nous voulons. Nous voulons être sûrs que les M, s’il y en a dans la chaîne, sont à son début.
  • M? - reconnaît un M optionnel. Comme nous le répétons trois fois, nous reconnaissons 0 à 3 M se suivant.
  • $ - reconnaît ce qui précède uniquement à la fin de la chaîne. Lorsqu’il est combiné avec ^ en début de motif, cela signifie que le motif doit correspondre à la chaîne entière, sans autres caractères avant ou après les M.
2 L’essence du module re est la fonction search, qui prend une expression régulière (pattern) et une chaîne ('M') qu’elle va tenter de faire correspondre. Si une correspondance est trouvée, search retourne un objet ayant diverses méthodes permettant de décrire la correspondance, sinon, search retourne None, la valeur nulle de Python. Tout ce qui nous intéresse à ce stade est de savoir si le motif est reconnu, ce que nous pouvons dire rien qu’en regardant la valeur retournée par search. 'M' est reconnu par cette expression régulière car le premier M optionnel correspond et que le second et troisième M sont ignorés.
3 'MM' est reconnu puisque les premier et deuxième M optionnels correspondent et que le troisième est ignoré.
4 'MMM' est reconnu puisque les trois M correspondent.
5 'MMMM' n’est pas reconnu. Les trois M correspondent, mais l’expression régulière précise la fin de chaîne (par le caractère $) et la chaîne ne s’arrête pas là (à cause du quatrième M). Donc search retourne None.
6 Un élément intéressant est qu’une chaîne vide est reconnue par l’expression régulière, puisque tous les M sont optionnels.

7.3.2. Rechercher les centaines

Les centaines présentent plus de difficultés que les milliers car elles peuvent être exprimées de plusieurs manières mutuellement exclusives, en fonction de leur valeur.

  • 100 = C
  • 200 = CC
  • 300 = CCC
  • 400 = CD
  • 500 = D
  • 600 = DC
  • 700 = DCC
  • 800 = DCCC
  • 900 = CM

Il y a donc quatre motifs possibles :

  • CM
  • CD
  • 0 à 3 C (0 si les centaines valent 0)
  • D, suivi de 0 à 3 C

Les deux derniers motifs peuvent être combinés en :

  • un D optionnel, suivi de 0 à 3 C

L’exemple suivant montre comment valider les centaines en chiffres romains.

Exemple 7.4. Rechercher les centaines

>>> import re
>>> pattern = '^M?M?M?(CM|CD|D?C?C?C?)$' 1
>>> re.search(pattern, 'MCM')            2
<SRE_Match object at 01070390>
>>> re.search(pattern, 'MD')             3
<SRE_Match object at 01073A50>
>>> re.search(pattern, 'MMMCCC')         4
<SRE_Match object at 010748A8>
>>> re.search(pattern, 'MCMC')           5
>>> re.search(pattern, '')               6
<SRE_Match object at 01071D98>
1 Ce motif commence de la même manière que le précédent, en vérifiant le début de chaîne (^), puis les milliers (M?M?M?). Ensuite vient la nouvelle partie, entre parenthèses, qui définit un ensemble de trois motifs mutuellement exclusifs séparés par des barres verticales : CM, CD, and D?C?C?C? (qui est un D optionnel suivi de 0 à 3 C optionnels). Le processeur d’expressions régulières teste chacun de ces motifs dans l’ordre (de gauche à droite), prend le premier qui correspond et ignore le reste.
2 'MCM' est reconnu car le premier M correspond, que le second et troisième M sont ignorés et que CM correspond (et donc les motifs CD et D?C?C?C? ne sont même pas examinés). MCM est la représentation de 1900.
3 'MD' est reconnu car le premier M correspond, les deuxième et troisième M sont ignorés et que le motif D?C?C?C? reconnaît D (chacun des trois C est optionnel et est ignoré). MD est la représentation de 1500.
4 'MMMCCC' est reconnu car les trois M correspondent et que le motif D?C?C?C? reconnaît CCC (le D est optionnel et est ignoré). MMMCCC est la représentation de 3300.
5 'MCMC' n’est pas reconnu. Le premier M correspond, les deuxième et troisième M sont ignorés et le CM correspond, mais le $ ne correspond pas car nous ne sommes pas encore à la fin de la chaîne (il nous reste le caractère C à évaluer). Le C ne correspond pas comme partie du motif D?C?C?C? car le motif CM a déja été reconnu et qu’ils sont mutuellement exclusifs.
6 Fait intéressant, une chaîne vide est toujours reconnue par ce motif, car tous les M sont optionnels et sont ignorés et que la chaîne vide est reconnue par le motif D?C?C?C? dans lequel tous les caractères sont optionnels et sont ignorés.

Ouf ! Vous voyez à quel point les expressions régulières peuvent devenir compliquées ? Et nous n’avons vu que les milliers et les centaines. Heureusement, si vous avez suivi jusque là, les dizaines sont relativement simples puisqu’elles suivent exactement le même motif. Mais continuons en examinant une autre manière d’exprimer ce motif.

Footnotes

[2] Note du traducteur : Je suppose que cet exemple suffit pour les lecteurs francophones.