Проверка на десятки и единицы

Теперь давайте расширим регулярное выражение чтобы включить десятки и единицы. Этот пример показывает проверку на десятки.

>>> pattern = '^M?M?M?(CM|CD|D?C?C?C?)(XC|XL|L?X?X?X?)$'
>>> re.search(pattern, 'MCMXL') ①
<_sre.SRE_Match object at 0x008EEB48>
>>> re.search(pattern, 'MCML') ②
<_sre.SRE_Match object at 0x008EEB48>
>>> re.search(pattern, 'MCMLX') ③
UNIQae610d7ca506639d-nowiki-0000004F-QINU
>>> re.search(pattern, 'MCMLXXX') ④
UNIQae610d7ca506639d-nowiki-00000050-QINU
>>> re.search(pattern, 'MCMLXXXX') ⑤
>>>

  • ① Тут патерн совпадает с началом строки, потом с первым опциональным символом М, потом CM, потом XL, потом с концом строки. Вспомните что синтаксис (A|B|C) означает «совпасть только с одним оз символов A, B или C» У нас совпадает XL, и мы игнорируем XC и L?X?X?X?, а после этого переходим к концу строки. MCMXL это римское представление числа 1940.
  • ② Тут патерн совпадает с началом строки, потом с первым опциональным символом М, потом CM, потом с L?X?X?X?. Из L?X?X?X? Совпадает L и пропускает три опциональных символа X. После этого переходит к концу строки. MCML это римское представление числа 1950.
  • ③ Тут патерн совпадает с началом строки, потом с первым опциональным символом М, потом CM, потом с опциональным L и первым опциональным X, пропуская второй и третий опциональные символы X, после этого переходит к концу строки. MCMLX это римское представление числа 1960.
  • ④ Тут патерн совпадает с началом строки, потом с первым опциональным символом М, потом CM, потом с опциональным L и всеми тремя опциональными символами X, после этого переходит к концу строки. MCMLXXX это римское представление числа 1980.
  • ⑤ Тут патерн совпадает с началом строки, потом с первым опциональным символом М, потом CM, потом с опциональным L и всеми тремя опциональными символами X, после этого не совпадает с концом строки, так как есть ещё один символ X, таким образом патерн не срабатывает и возвращает None. MCMLXXXX это недопустимое римское число.


(A|B) совпадает либо с A либо с B.

Для описания единиц подходит тот же патерн. Я уменьшу детализацию и покажу конечный результат.

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

Итак как это будет выглядеть используя альтернативный синтаксис {n,m}? Этот пример показывает новый синтаксис.

>>> pattern = '^M{0,3}(CM|CD|D?C{0,3})(XC|XL|L?X{0,3})(IX|IV|V?I{0,3})$'
>>> re.search(pattern, 'MDLV') ①
UNIQae610d7ca506639d-nowiki-00000053-QINU
>>> re.search(pattern, 'MMDCLXVI') ②
UNIQae610d7ca506639d-nowiki-00000054-QINU
>>> re.search(pattern, 'MMMDCCCLXXXVIII') ③
UNIQae610d7ca506639d-nowiki-00000055-QINU
>>> re.search(pattern, 'I') ④
UNIQae610d7ca506639d-nowiki-00000056-QINU

  • ① Тут патерн совпадает с началом строки, потом с одним из трёх возможных символов М, потом D?C{0,3}. Из них совпадает только опциональное D и ни один из опциональных C. Далее совпадает опциональное L из L?X{0,3} и ни один из трёх опциональных X. После совпадает с V из V?I{0,3} и ни с одним из трёх опциональных I и наконец с концом строки. MDLV это римское представление числа 1555.
  • ② Тут патерн совпадает с началом строки, потом с двумя из трёх возможных символов М, потом D и один опциональный C из D?C{0,3}. Потом L?X{0,3} с L и один из трёх возможных X, потом V?I{0,3} с V и одним из трёх I, потом с концом строки. MMDCLXVI это римское представление числа 2666.
  • ③ Тут патерн совпадает с началом строки, потом с тремя из трёх M, потом D и C из D?C{0,3}, потом L?X{0,3} с L и три из трёх X, потом V?I{0,3} с V и тремя из трёх I, потом конец строки. MMMDCCCLXXXVIII это римское представление числа 3888, и это максимально длинное римское число которое можно записать без расширенного синтаксиса.
  • ④ Смотрите внимательно. (Я чувствую себя магом, «Смотрите внимательно детки, сейчас кролик вылезет из моей шляпы ;)» Тут совпадает начало строки, ни один из трёх М, потом D?C{0,3} пропускает опциональный D и три опциональных C, потом L?X{0,3} пропуская опциональный L и три опциональных X,потом V?I{0,3} пропуская опциональный V и один из трёх опциональных I. Потом конец строки. Стоп, фуф.


Если вы следовали всему и поняли с первой попытки, значит у вас получается лучше чем у меня. Теперь представьте что вы пытаетесь разобраться в чьих то регулярных выражениях в важной функции в рамках огромной программы. Или например представьте что вы возвращаетесь к собственной программе через несколько месяцев. Я делал это и это не слишком приятное зрелище.

А сейчас давайте исследуем альтернативный синтаксис, который позволит легче выполнять поддержку ваших выражений.