① Тут патерн совпадает с началом строки, потом с первым опциональным символом М, потом 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.
Для описания единиц подходит тот же патерн. Я уменьшу детализацию и покажу конечный результат.
① Тут патерн совпадает с началом строки, потом с одним из трёх возможных символов М, потом 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. Потом конец строки. Стоп, фуф.
Если вы следовали всему и поняли с первой попытки, значит у вас получается лучше чем у меня. Теперь представьте что вы пытаетесь разобраться в чьих то регулярных выражениях в важной функции в рамках огромной программы. Или например представьте что вы возвращаетесь к собственной программе через несколько месяцев. Я делал это и это не слишком приятное зрелище.
А сейчас давайте исследуем альтернативный синтаксис, который позволит легче выполнять поддержку ваших выражений.
⁂
Подробные регулярные выражения
До сих пор вы имели дело с тем что я называю «компактными» регулярными выражениями. Как вы могли заметить они трудны для прочтения, даже если вы понимаете что они делают. Нет гарантии что вы сможете разобраться в них спустя шесть месяцев. Что вам действительно необходимо так это вложенная документация
Python позволяет вам сделать это при помощи подробных регулярных выражений. Подробные регулярные выражения отличаются от компактных двумя способами:
Пустые строки игнорируются, пробелы, табы и возвраты каретки не совпадают соответственно. Они вообще не совпадают. (Если вы хотите совпадения с пробелом в подробном регулярном выражении, вам необходимо поставить бэкслэш перед ним. )
Комментарии игнорируются. Комментарий в подробном регулярном выражении такой же как и комментарий в коде Python: он начинается с символа # и действует до конца строки. В этом случае этото комментарий это комментарий внутри многострочной строки, но он работает также как и простой.
Пример сделает это более понятным. Давайте перепроверим компактное регулярное выражение с которым мы работали и создадим подробное регулярное выражение. Этот пример показан ниже.
> > > pattern = ''' ^ # начало строки M{0, 3} # тысячи - 0 до 3 M (CM|CD|D? C{0, 3}) # сотни — 900 (CM), 400 (CD), 0-300 (0 до 3 C), # или 500-800 (D, с последующими от 0 до 3 C) (XC|XL|L? X{0, 3}) # десятки - 90 (XC), 40 (XL), 0-30 (0 до 3 X), # или 50-80 (L, с последующими от 0 до 3 X) (IX|IV|V? I{0, 3}) # единицы - 9 (IX), 4 (IV), 0-3 (0 до 3 I), # или 5-8 (V, с последующими от 0 до 3 I) $ # конец строки ''' > > > re. search(pattern, 'M', re. VERBOSE) ① UNIQae610d7ca506639d-nowiki-00000058-QINU > > > re. search(pattern, 'MCMLXXXIX', re. VERBOSE) ② UNIQae610d7ca506639d-nowiki-00000059-QINU > > > re. search(pattern, 'MMMDCCCLXXXVIII', re. VERBOSE) ③ < _sre. SRE_Match object at 0x008EEB48> > > > re. search(pattern, 'M') ④
① Главное что надо запомнить, это то что необходимо добавлять экстра аргументы для работы с ними: re. VERBOSE это константа определённая в модуле re которая служит сигналом что патерн должен быть использован как подробное регулярное выражение. Как вы можете видеть, этот патерн содержит большое количество пустых строк. (и все они игнорируются), а также несколько комментариев (которые игнорируются также). Если мы игнорируем комментарии и пустые строки, то получается то же самое регулярное выражение что и в предыдущем примере, но в гораздо более читабельном виде.
② Здесь совпадает начало строки, потом одно и трёх возможных M, потом CM, потом L и три из возможных X, потом IX, потом конец строки.
③ Здесь совпадает начало строки, потом три из трёх возможных M, потом D и три из возможных трёх C, потом L и три из трёх возможных X, потом V и три из трёх возможных I, потом конец строки.
④ Тут не совпадает. Почему? Так как отсутствует флаг re. VERBOSE и функция re. search рассматривает патерн как компактное регулярное выражение, с значащими пробелами и символами #. Python не может автоматически определить является ли регулярное выражение подробным или нет. Python рассматривает каждое регулярное выражение как компактное до тех пор пока вы не укажете что оно подробное.