HOWTO по регулярным выражениям

Автор:A.M. Kuchling <amk@amk.ca>

Аннотация

Этот документ является вводным руководством по использованию регулярных выражений в Python с модулем re. Он предоставляет более мягкое введение, чем соответствующий раздел в справочнике библиотеки.

Введение

Регулярные выражения (называемые RE, или regexes, или regex шаблоны) по существу являются крошечным, узкоспециализированным языком программирования, встроенным в Python и доступным через модуль re. Используя этот маленький язык, вы указываете правила для набора возможных строки, которые вы хотите сопоставить; этот набор может содержать английские предложения, адреса электронной почты, команды TeX или что угодно. Вы можете тогда задать вопросы, такие как «Этот строка, соответствует образцу?», или «Там достойно образца где-нибудь в этой последовательности?». Можно также использовать RE для изменения или разделения строка различными способами.

Шаблоны регулярного выражения компилируются в последовательность байт-кодов, которые затем выполняются соответствующим механизмом, записанным на C. Для расширенного использования может потребоваться обратить пристальное внимание на то, как механизм будет выполнять данный RE, и записать RE определенным образом, чтобы получить байт-код, который работает быстрее. Оптимизация не рассматривается в этом документе, поскольку для этого требуется хорошее понимание внутренних компонентов соответствующего модуля.

Язык регулярных выражений относительно мал и ограничен, поэтому не все возможные задачи обработки строка можно выполнить с помощью регулярных выражений. Есть также задачи, которые can делать регулярными выражениями, но выражения оказываются очень сложными. В этих случаях вам может быть лучше написать Python код для выполнения обработки; хотя Python код будет медленнее, чем сложное регулярное выражение, оно также, вероятно, будет более понятным.

Простые шаблоны

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

Подробное объяснение компьютерной науки, лежащей в основе регулярных выражений (детерминированные и недетерминированные конечные автоматы), можно найти практически в любом учебнике по написанию компиляторов.

Символы соответствия

Большинство букв и символов просто совпадут между собой. Например, регулярное выражение test точно совпадает с строка test. (Можно включить режим без учета регистра, который позволит RE также соответствовать Test или TEST; подробнее об этом позже.)

В этом правиле имеются исключения; некоторые знаки - специальный metacharacters и не соответствуют себе. Вместо этого они сигнализируют, что некоторые нестандартные вещи должны быть сопоставлены, или они влияют на другие части RE, повторяя их или изменяя их значение. Значительная часть этого документа посвящена обсуждению различных метасимволов и того, что они делают.

Вот полный список метасимволов; их значения будут обсуждаться в остальной части настоящего документа HOWTO.

. ^ $ * + ? { } [ ] \ | ( )

Сначала рассматрим метасимволы, которые будут [ и ]. Они являются используемый для указания класса символ, который представляет собой набор символов, которые требуется сопоставить. Символы могут быть перечислены по отдельности, или диапазон символов может быть указан путем указания двух символов и разделения их по '-'. Например, [abc] будет соответствовать любому из символов a, b или c; это то же самое, что и [a-c], который использует диапазон для выражения одного и того же набора символов. Если бы вы хотели сопоставить только строчные буквы, ваш RE был бы [a-z].

Метасимволы не активны внутри классы. Например, [akm$] будет соответствовать любому из символов 'a', 'k', 'm' или '$'; '$' обычно является метасимволом, но внутри символ класс он лишен своей особой природы.

Вы можете соответствовать знакам, не перечисленным в класс complementing набор. На это указывает включение '^' в качестве первого символ класс. Например, [^5] будет соответствовать любому символ, кроме '5'. Если курсор появляется в другом месте класса символ, он не имеет особого значения. Например: [5^] будет соответствовать либо '5', либо '^'.

Возможно, самый важный метасимвол - обратная косая черта, \. Как и в литералах Python строка, за обратной косой чертой могут следовать различные символы для сигнализации различных специальных последовательностей. Это также используемый избежать всех метасимволов, так что вы все еще можете сопоставить их в узорах; например, если необходимо сопоставить [ или \, перед ними можно задать обратную косую черту, чтобы удалить их особое значение: \[ или \\.

Некоторые специальные последовательности, начинающиеся с '\', представляют заранее определенные наборы символов, которые часто полезны, такие как набор цифр, набор букв или набор чего-либо, что не является пробелом.

Рассмотрим пример: \w соответствует любой буквенно-цифровой символ. Если шаблон regex выражен в байтах, это эквивалентно класс [a-zA-Z0-9_]. Если шаблон regex является строкой, \w будет соответствовать всем символам, помеченным как буквы в базе данных юникода, предоставленной модулем unicodedata. Вы можете использовать более ограниченное определение \w в образце строка, поставляя флаг re.ASCII, компилируя регулярное выражение.

Следующий список специальных последовательностей не является полным. Для полного списка последовательностей и расширенных определений класс для узоров Unicode строка, посмотрите последнюю часть Синтаксис регулярных выражений в стандартной ссылке библиотеки. В целом версии Unicode соответствуют любому символ, это находится в соответствующей категории в базе данных Unicode.

\d
Соответствует любой десятичной цифре; это эквивалентно классу [0-9].
\D
Соответствует любому не цифровому символу; это эквивалентно классу [^0-9].
\s
Соответствует любому символу пробела; это эквивалентно классу [\t\n\r\f\v].
\S
Соответствует любому символу, отличному от пробела; это эквивалентно классу [^\t\n\r\f\v].
\w
Соответствует любому буквенно-цифровому символу; это эквивалентно классу [a-zA-Z0-9_].
\W
Соответствует любому не буквенно-цифровому символу; это эквивалентно классу [^a-zA-Z0-9_].

Эти последовательности могут быть включены внутри символ класс. Например, [\s,.] - это символ класс, который будет соответствовать любому символу пробела, или ',', или '.'.

Последний метасимвол в этом разделе - .. Он соответствует всему, кроме символа новой строки, и существует альтернативный режим (re.DOTALL), где он будет соответствовать даже новой строке. . часто - используемый, где вы хотите соответствовать «любому характеру».

Повторение материала

Возможность соответствия различным наборам символов - это первое, что регулярные выражения могут сделать, что невозможно с помощью методы, доступных на строки. Тем не менее, если бы это был единственный дополнительный потенциал регексов, они не были бы большим шагом вперед. Другая возможность состоит в том, что можно указать, что части RE должны повторяться определенное количество раз.

Первый метасимвол для повторения вещей, на которые мы будем смотреть - это *. * не соответствует литерал символ '*'; вместо этого указывается, что предыдущая символ может быть сопоставлена ноль или более раз, а не ровно один раз.

Например, ca*t будет соответствовать 'ct' (0 'a' символов), 'cat' (1 'a'), 'caaat' (3 'a' символов) и т.д.

Такие повторения, как *, являются greedy; при повторении RE соответствующий механизм будет пытаться повторить его как можно больше раз. Если более поздние части шаблона не совпадают, соответствующий обработчик выполнит резервное копирование и повторит попытку с меньшим количеством повторений.

Пошаговый пример сделает это более очевидным. Рассмотрим выражение a[bcd]*b. Это соответствует букве 'a', нулю или более букв из класс [bcd], и, наконец, заканчивается на 'b'. Теперь представьте себе сопоставление этого RE с строка 'abcbd'.

Шаг Соответствие Объяснение
1 a a в соответствии RE.
2 abcbd Движок соответствия [bcd]*, просматривая в даль, как только можно, до конца строки.
3 Сбой Двигатель пытается соответствовать b, но текущее положение в конце строки, таким образом, это приведёт к ошибке.
4 abcb Вернуться назад, так, чтобы [bcd]* соответствовало на однин символ меньше.
5 Сбой Попробовать b повторно, но текущее положение в последнем символе, который является 'd'.
6 abc Вернуться назад повторно, чтобы [bcd]* совпадал только с bc.
6 abcb Попробовать b повторно. На этот раз символ в текущей позиции равен 'b', поэтому успешно.

Конец RE уже достигнут, и он соответствует 'abcb'. Это демонстрирует, как механизм сопоставления проходит как можно дальше, и если совпадение не найдено, он будет постепенно выполнять резервное копирование и повторять остальные RE снова и снова. Он будет резервировать, пока не попробует ноль матчей для [bcd]*, и если это впоследствии выйдет из строя, движок сделает вывод, что строка не соответствует RE вообще.

Другим повторяющимся метасимволом является +, который совпадает один или несколько раз. внимательно следить за различиями между * и +; * совпадает ноль или более раз, поэтому то, что повторяется, может вообще отсутствовать, в то время как + требует по крайней мере один. Чтобы использовать аналогичный пример, ca+t будет соответствовать 'cat' (1 'a'), 'caaat' (3 'a's), но не будет соответствовать 'ct'.

Есть еще два повторяющихся отборочных. Символ вопросительного знака, ?, совпадает один раз или ноль раз; вы можете думать о том, что это помечает что-то как необязательное. Например, home-?brew соответствует либо 'homebrew', либо 'home-brew'.

Наиболее сложным повторным квалификатором является {m,n}, где m и n - десятичные целые числа. Этот квалификатор означает, что должно быть не менее m повторений и не более n. Например, a/{1,3}b будет соответствовать 'a/b', 'a//b' и 'a///b'. Он не будет соответствовать 'ab', который не имеет косых черт, или 'a////b', который имеет четыре.

Можно опустить либо m, либо n; в этом случае для отсутствующего значения принимается приемлемое значение. Опущение m интерпретируется как нижний предел 0, в то время как опущение n приводит к верхней границе бесконечности.

Читатели редукционистского изгиба могут заметить, что все три других квалификатора могут быть выражены с помощью этой нотации. {0,} то же, что и *, {1,} эквивалентно +, а {0,1} то же, что и ?. Лучше использовать *, + или ?, когда можно, просто потому, что они короче и проще в чтении.

Использование регулярных выражений

Теперь, когда мы рассмотрели некоторые простые регулярные выражения, как мы можем использовать их в Python? модуль re предоставляет интерфейс для механизма регулярных выражений, позволяющий компилировать RE в объекты, а затем выполнять с ними совпадения.

Компилирование регулярных выражений

Регулярные выражения компилируются в объекты шаблона, которые содержат методы для различных операций, таких как поиск совпадений шаблонов или выполнение замещений строк.:

>>> import re
>>> p = re.compile('ab*')
>>> p
re.compile('ab*')

re.compile() также принимает необязательный аргумент flags используемый, чтобы включить различные особенности и изменения синтаксиса. Мы рассмотрим доступные настройки позже, но на данный момент один пример:

>>> p = re.compile('ab*', re.IGNORECASE)

RE передается re.compile() как строка. RE обрабатываются как строки, поскольку регулярные выражения не являются частью основного языка Python, и для их выражения не был создан специальный синтаксис. (Есть приложения, которые вообще не нуждаются в RE, поэтому нет необходимости раздувать спецификацию языка, включая их.) вместо этого модуль re является просто модулем расширения C, входящим в состав Python, подобно модулям socket или zlib.

Вставка RE в строку делает язык Python более простым, но имеет один недостаток, который является темой следующего раздела.

Про обратный слеш

Как указано ранее, регулярные выражения используют обратную косую черту символ ('\'), чтобы указать на специальные формы или позволить специальным знакам быть используемый, не призывая их специальное значение. Это находится в противоречии с использованием Python’s того же символ для той же цели в литералах строк.

Допустим, вы хотите написать RE, совпадающее с строка \section, которое может быть найдено в LaTeX-файле. Чтобы выяснить, что написать в кодексе программы, начните с желаемого строка быть согласованными. Затем необходимо избежать любых обратных косых черт и других метасимволов, предшествуя им обратной косой чертой, что приводит к строка \\section. Получающийся строка, который должен быть передан к re.compile(), должен быть \\section. Однако, чтобы выразить это как Python строковый литерал, обе обратные косые черты должны быть экранированы повторно.

Символы Стадия
\section Текстовая строка для соответствия
\\section Экранирование обратного слеша для re.compile()
"\\\\section" Экранирование обратных слешей для строкового литерала

Короче говоря, чтобы соответствовать обратной косой черте литерал, нужно написать '\\\\' как последовательность RE, потому что регулярное выражение должно быть \\, и каждая обратная косая черта должна быть выражена как \\ в регулярном Python строка литерал. В RE, которые неоднократно показывают обратные косые черты, это приводит к большому количеству повторных обратных косых черт и делает получающееся трудное строки, чтобы понять.

Решение состоит в том, чтобы использовать сырое примечание строка Python’s для регулярных выражений; обратные косые черты не обработаны никаким специальным способом в строка литерал, снабженном префиксом 'r', таким образом, r"\n" - два-символ строка, содержащий '\' и 'n', в то время как "\n" - один-символ строка, содержащий символ новой строки. Регулярные выражения будут часто писаться в Python код, используя это сырое примечание строка.

Кроме того, специальные последовательности побега, которые действительны в регулярных выражениях, но не действительны как опечатки Python строка, теперь приводят к DeprecationWarning и в конечном счете станут SyntaxError, что означает, что последовательности будут недействительны, если сырое примечание строка или экранирование обратных косых черт не будут используемый.

Обычная строка Сырая строка
"ab*" r"ab*"
"\\\\section" r"\\section"
"\\w+\\s+\\1" r"\w+\s+\1"

Выполнение соответствий

Что делать с объектом, представляющим скомпилированное регулярное выражение? объекты массива имеют несколько методы и атрибуты. Здесь будут охвачены только самые значительные из них; для получения полного списка обратитесь к re документам.

Метод/Атрибут Назначение
match() Определить, соответствует ли RE началу строки.
search() Просканировать строку и найти любое место соответствия RE.
findall() Найти все подстроки соответствующие RE и вернуть их в виде списка.
finditer() Найти все подстроки соответствующие RE и вернуть их как итератор.

match() и search() return None, если не удается найти совпадение. Если они успешны, возвращается объект соответствия сущность, содержащий информацию о матче: где он начинается и заканчивается, подстрока, которой он соответствует, и многое другое.

Вы можете узнать об этом, интерактивно экспериментируя с модулем re. Если вы имеете tkinter в наличии, вы можете также хотеть посмотреть на Tools/demo/redemo.py, демонстрационная программа, включенная с распределением Python. Он позволяет вводить RE и строки и показывает, соответствует RE или нет. redemo.py может быть весьма полезным при попытке отладки сложной RE.

Это HOWTO использует стандартый Python интерпретатор для своих примеров. Сначала запустите интерпретатор Python, импортируйте модуль re и скомпилируйте RE:

>>> import re
>>> p = re.compile('[a-z]+')
>>> p
re.compile('[a-z]+')

Теперь, вы можете попытаться соответствовать различному строки против RE [a-z]+. Пустое строка не должно совпадать вообще, поскольку + означает «одно или несколько повторений». В этом случае match() должен возвращать None, что приведет к тому, что интерпретатор не будет выводиться. Вы можете явно напечатать результат match(), чтобы сделать это ясно.:

>>> p.match("")
>>> print(p.match(""))
None

Теперь давайте попробуем его на строка, что он должен соответствовать, например, tempo. В этом случае match() вернет объект соответствия, поэтому результат следует сохранить в переменной для последующего использования:

>>> m = p.match('tempo')
>>> m
<re.Match object; span=(0, 5), match='tempo'>

Теперь вы можете подвергнуть сомнению объект соответствия для получения информации о соответствии строка. У объекта матча сущности также есть несколько методы и признаки; наиболее важными из них являются:

Метод/Атрибут Назначение
group() Вернуть строку, совпадающую с RE
start() Вернуть стартовую позицию соответствия
end() Вернуть конечную позицию соответствия
span() Вернуть кортеж, содержащий (начальные, конечные) позиции соответствия

Попытка этих методы скоро прояснит их смысл:

>>> m.group()
'tempo'
>>> m.start(), m.end()
(0, 5)
>>> m.span()
(0, 5)

group() возвращает подстроку, соответствующую RE. start() и end() возвращают начальный и конечный индекс матча. span() возвращает как начальные, так и конечные индексы в одном кортеже. Поскольку match() метод проверяет только то, соответствует ли RE в начале строки, start() всегда будет равен нулю. Однако search() метод шаблонов сканирует строку, поэтому в этом случае совпадение может не начинаться с нуля:

>>> print(p.match('::: message'))
None
>>> m = p.search('::: message'); print(m)
<re.Match object; span=(4, 11), match='message'>
>>> m.group()
'message'
>>> m.span()
(4, 11)

В реальных программах наиболее распространенным стилем является сохранение объекта соответствия в переменной, а затем проверка, была ли она None. Обычно так выглядит:

p = re.compile( ... )
m = p.match( 'string goes here' )
if m:
    print('Match found: ', m.group())
else:
    print('No match')

Два образца методы возвращают все матчи для образца. findall() возвращает список совпадающих строк:

>>> p = re.compile(r'\d+')
>>> p.findall('12 drummers drumming, 11 pipers piping, 10 lords a-leaping')
['12', '11', '10']

Префикс r, делающий литерал сырьевым строка литералом, необходим в этом примере, потому что escape-последовательности в нормальном «приготовленном» строка литерал, которые не распознаются Python, в отличие от регулярных выражений, теперь приводят к DeprecationWarning и в конечном итоге станут SyntaxError. См. Про обратный слеш.

findall() должен создать весь список, прежде чем его можно будет вернуть в качестве результата. finditer() метод возвращает последовательность объект соответствия сущности в виде итератора:

>>> iterator = p.finditer('12 drummers drumming, 11 ... 10 ...')
>>> iterator  
<callable_iterator object at 0x...>
>>> for match in iterator:
...     print(match.span())
...
(0, 2)
(22, 24)
(29, 31)

Функции уровня модуля

Нет необходимости создавать объект шаблона и вызывать его методы; модуль re также обеспечивает функции верхнего уровня по имени match(), search(), findall(), sub(), и т.д. Эти функции принимают те же аргументы, что и соответствующий шаблон метод с добавленным RE строка в качестве первого аргумента, и по-прежнему возвращают либо None, либо сущность объекта соответствия:

>>> print(re.match(r'From\s+', 'Fromage amk'))
None
>>> re.match(r'From\s+', 'From amk Thu May 14 19:12:10 1998')  
<re.Match object; span=(0, 5), match='From '>

Под капотом эти функции просто создают объект образца для вас и называют соответствующий метод на нем. Они также сохраняют скомпилированный объект в кэше, поэтому будущим вызовам с использованием того же RE не нужно будет разбирать шаблон снова и снова.

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

Флаги компиляции

Флаги компиляции позволяют изменять некоторые аспекты работы регулярных выражений. Флаги доступны в модуле re под двумя именами, длинным именем, таким как IGNORECASE, и короткой однобуквенной формой, такой как I. (Если вы знакомы с модификаторами шаблона Perl, однобуквенные формы используют одни и те же буквы; краткая форма re.VERBOSE - re.X, например.) можно задать несколько флагов путем побитового ИЛИ; re.I | re.M устанавливает флаги I и M, например.

Вот таблица доступных флагов, а затем более подробное объяснение каждого из них.

Флаг Значение
ASCII, A Сделать некоторые экранированные символы, подобные \w, \b, \s и \d, с особым свойством, соответствовать обычным ASCII символам.
DOTALL, S Сделать . совпадающим с любым символом, включая новые строки.
IGNORECASE, I Выполнить нечувствительные к регистру совпадения.
LOCALE, L Выполнить совпадение с учётом локали.
MULTILINE, M Многострочное сопоставление, влияющее на ^ и $.
VERBOSE, X (for „extended“) Включить подробные RE, которые могут быть более человекочитаемыми и понятными.
I
IGNORECASE

Выполнить согласование без учета регистра; символ класс и литерал строки будут соответствовать буквам, игнорируя регистр. Например, [A-Z] также будет соответствовать строчным буквам. Полное соответствие Unicode также работает, если флаг ASCII не используемый, чтобы отключить матчи неASCII. Когда узоры Unicode, [a-z] или [A-Z] - используемый в сочетании с флагом IGNORECASE, они будут соответствовать 52 письмам о ASCII и 4 дополнительным письмам неASCII: „İ“ (U+0130, латинское письмо I прописной с точкой выше), „ı“ (U+0131, латинская строчная буква, без точки i), „ſ“ (U+017F, латинская строчная буква длинный s) и „K“ (U+212A символ Кельвина). Spam будет соответствовать 'Spam', 'spam', 'spAM' или 'ſpam' (последний сопоставляется только в режиме юникод). Этот нижний регистр не учитывает текущий языковой стандарт; это произойдет, если также установить флаг LOCALE.

L
LOCALE

Сделайте \w, \W, \b, \B и соответствие без учета регистра зависящему от текущего места действия вместо базы данных Unicode.

Языковые стандарты - это функция библиотеки си, предназначенная для помощи в написании программ, учитывающих языковые различия. Например, если бы вы обрабатываете французский текст кодированный, вы хотели бы написать \w+, чтобы распознать слова, но \w только соответствует символ класс [A-Za-z] в узорах байтов; он не будет соответствовать байтам, соответствующим é или ç. Если система настроена правильно и выбран французский языковой стандарт, некоторые функции C сообщат программе, что байт, соответствующий é, также должен считаться буквой. Установка флага LOCALE при компиляции регулярного выражения приведет к тому, что результирующий скомпилированный объект будет использовать эти функции C для \w; это медленнее, но также позволяет \w+ соответствовать французским словам, как вы ожидали. Использование этого флага не рекомендуется в Python 3, так как механизм языкового стандарта очень ненадежен, он обрабатывает только одну «культуру» за раз, и работает только с 8-битными языками. Сопоставление юникода уже включено по умолчанию в Python 3 для шаблонов юникода (str), и он может обрабатывать различные языковые стандарты/языки.

M
MULTILINE

(^ и $ еще не были объяснены; они будут представлены в разделе Больше метасимволов.)

Обычно ^ соответствует только в начале последовательности, и $ соответствует только в конце строка и немедленно перед новой строкой (если таковые имеются) в конце строка. Когда этот флаг определен, матчи ^ в начале строка и в начале каждой линии в последовательности, сразу после каждого newline. Точно так же метахарактер $ соответствует любому в конце строка и в конце каждой линии (немедленно предшествующий каждому newline).

S
DOTALL

Делает '.' специальным символ совпадающим с любым символ вообще, включая новую строку; без этого флага '.' будет соответствовать чему-либо за исключением новой строки.

A
ASCII

Сделайте \w, \W, \b, \B, \s и \S выполняют соответствие только для ASCII вместо полного соответствия Unicode. Это имеет значение только для шаблонов юникода и игнорируется для шаблонов байтов.

X
VERBOSE

Этот флаг позволяет писать регулярные выражения, которые являются более читаемыми, предоставляя больше гибкости в том, как их можно форматировать. Когда этот флаг был определен, пробел в RE, строка проигнорирован, кроме тех случаев, когда пробел находится в символ класс или предшествовал несбежавшей обратной косой чертой; это позволяет более четко упорядочить и отступить RE. Этот флаг также позволяет поместить комментарии в RE, которые будут игнорироваться обработчиком; комментарии отмечены '#', которым это не находится ни один в символ класс или предшествовало несбежавшей обратной косой чертой.

Например, вот RE, в котором используется re.VERBOSE; посмотреть, насколько проще читать?:

charref = re.compile(r"""
 &[#]                # Start of a numeric entity reference
 (
     0[0-7]+         # Octal form
   | [0-9]+          # Decimal form
   | x[0-9a-fA-F]+   # Hexadecimal form
 )
 ;                   # Trailing semicolon
""", re.VERBOSE)

Без подробной настройки RE будет выглядеть так:

charref = re.compile("&#(0[0-7]+"
                     "|[0-9]+"
                     "|x[0-9a-fA-F]+);")

В вышеупомянутом примере Python’s автоматическая связь опечаток строка была используемый, чтобы разбить RE в мелкие кусочки, но еще более трудно понять, чем версия, используя re.VERBOSE.

Более продвинутые шаблоны

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

Больше метасимволов

Есть метасимволы, которые мы еще не покрыли. Большинство из них будут освещены в этом разделе.

Некоторые из остальных метасимволов, которые будут обсуждаться, являются zero-width assertions. Они не заставляют движок продвигаться по струне; вместо этого они вообще не потребляют никаких символов и просто преуспевают или терпят неудачу. Например, \b - это утверждение, что текущая позиция расположена на границе слова; положение совсем не изменяется \b. Это означает, что утверждения нулевой ширины никогда не должны повторяться, потому что если они совпадают один раз в данном месте, очевидно, что они могут быть сопоставлены бесконечное количество раз.

|

Чередование или оператор «или». Если A и B являются регулярными выражениями, A|B будет соответствовать любому строка, который соответствует A или B. | имеет очень низкий приоритет, чтобы сделать его работать разумно, когда вы чередуетесь много-символьные строки. Crow|Servo будет соответствовать 'Crow' или 'Servo', а не 'Cro', 'w' или 'S', и 'ervo'.

Чтобы сопоставить литерал '|', используйте \| или заключите его в класс символ, как в [|].

^

Соответствие началу строки. Если не установлен флаг MULTILINE, он будет совпадать только в начале строка. В режиме MULTILINE это также совпадает сразу после каждой новой строки внутри строка.

Например, если требуется сопоставить слово From только в начале строки, используется значение RE ^From.:

>>> print(re.search('^From', 'From Here to Eternity'))  
<re.Match object; span=(0, 4), match='From'>
>>> print(re.search('^From', 'Reciting From Memory'))
None

Чтобы сопоставить литерал '^', используйте команду \^.

$

Соответствует концу строки, которая определяется как конец строки, или в любом месте, за которым следует новая строка символ:

>>> print(re.search('}$', '{block}'))  
<re.Match object; span=(6, 7), match='}'>
>>> print(re.search('}$', '{block} '))
None
>>> print(re.search('}$', '{block}\n'))  
<re.Match object; span=(6, 7), match='}'>

Чтобы сопоставить литерал '$', используйте команду \$ или заключите его в класс символов, такой как [$].

\A
Совпадение только в начале строки. Когда не в режиме MULTILINE, \A и ^ фактически одинаковы. В режиме MULTILINE они разные: \A всё ещё совпадает только в начале строки, но ^ может совпадать в любом месте внутри строка, следующего за новой строкой символ.
\Z
Совпадение только в конце строки.
\b

Граница слова. Это утверждение нулевой ширины, совпадающее только с началом или концом слова. Слово определяется как последовательность буквенно- цифровых символов, поэтому конец слова обозначается пробелом или не буквенно- цифровым символ.

Следующий пример соответствует class только в том случае, если это полное слово; он не будет совпадать, когда он содержится внутри другого слова.:

>>> p = re.compile(r'\bclass\b')
>>> print(p.search('no class at all'))
<re.Match object; span=(3, 8), match='class'>
>>> print(p.search('the declassified algorithm'))
None
>>> print(p.search('one subclass is'))
None

Есть две тонкости, которые вы должны помнить при использовании этой специальной последовательности. Во-первых, это наихудшее столкновение между Python’s строка литералами и последовательностями регулярного выражения. В литералах Python’s строка \b - знак возврата на одну позицию, стоимость ASCII 8. Если вы не используете необработанные строки, то Python будет преобразовывать \b в заднее пространство, и ваш RE не будет соответствовать, как вы ожидаете. Следующий пример выглядит так же, как наш предыдущий RE, но опускает 'r' перед RE строкой.:

>>> p = re.compile('\bclass\b')
>>> print(p.search('no class at all'))
None
>>> print(p.search('\b' + 'class' + '\b'))
<re.Match object; span=(0, 7), match='\x08class\x08'>

Во-вторых, внутри класса символ, где это утверждение не используется, \b представляет символ backspace для совместимости с литералами Python’s строка.

\B
Другое утверждение нулевой ширины, это противоположно \b, совпадающее только в том случае, если текущая позиция не находится на границе слова.

Группировка

Часто вам нужно получить больше информации, чем просто совпадение RE или not. регулярные выражения часто используемый, чтобы рассечь строки, написав RE, разделенный на несколько подгрупп, которые соответствуют различным интересующим компонентам. Например, строка заголовка RFC-822 разделяется на имя заголовка и значение, разделенное символом ':', например:

From: author@example.com
User-Agent: Thunderbird 1.5.0.9 (X11/20061227)
MIME-Version: 1.0
To: editor@example.com

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

Группы отмечены метасимвлами '(', ')'. '(' и ')' имеют во многом то же значение, что и в математических выражениях; они группируют содержащиеся в них выражения, и можно повторить содержимое группы с помощью повторяющегося квалификатора, например *, +, ? или {m,n}. Например, (ab)* будет соответствовать нулю или более повторов ab.:

>>> p = re.compile('(ab)*')
>>> print(p.match('ababababab').span())
(0, 10)

Группы, обозначенные '(', ')', также фиксируют начальный и конечный индекс текста, который они соответствуют; это можно извлечь, передав аргумент group(), start(), end() и span(). Группы нумеруются, начиная с 0. Всегда присутствует группа 0; это - целое RE, таким образом, объект соответствия методы у всех есть группа 0 как их аргумент по умолчанию. Позже мы увидим, как выразить группы, которые не фиксируют диапазон текста, который они соответствуют.:

>>> p = re.compile('(a)b')
>>> m = p.match('ab')
>>> m.group()
'ab'
>>> m.group(0)
'ab'

Подгруппы нумеруются слева направо, от 1 вверх. Группы могут быть вложенными; чтобы определить число, просто посчитайте символы открывающих скобок, идущие слева направо.:

>>> p = re.compile('(a(b)c)d')
>>> m = p.match('abcd')
>>> m.group(0)
'abcd'
>>> m.group(1)
'abc'
>>> m.group(2)
'b'

group() можно передавать несколько номеров групп одновременно, и в этом случае он возвращает кортеж, содержащий соответствующие значения для этих групп:

>>> m.group(2,1,2)
('b', 'abc', 'b')

groups() метод возвращает кортеж, содержащий строки для всех подгрупп, от 1 до любого количества.:

>>> m.groups()
('abc', 'b')

Обратные ссылки в шаблоне позволяют указать, что содержимое более ранней группы захвата также должно быть найдено в текущем расположении строка. Например, \1 будет успешным, если точное содержимое группы 1 может быть найдено в текущей позиции, и не удастся в противном случае. Помните, что опечатки Python’s строка также используют обратную косую черту, сопровождаемую числами, чтобы позволить включая произвольные знаки в последовательности, так, несомненно, будут использовать сырой строка, включая обратные ссылки в RE.

Например, следующий RE обнаруживает удвоенные слова в строка:

>>> p = re.compile(r'\b(\w+)\s+\1\b')
>>> p.search('Paris in the the spring').group()
'the the'

Обратные ссылки как это не часто полезны для того, чтобы просто перерыть строка — есть немного текстовых форматов, которые повторяют данные таким образом —, но вы скоро узнаете, что они very полезный, выполняя замены строка.

Не захватывающие и именованные группы

Сложные RE могут использовать множество групп, как для захвата представляющих интерес подстрок, так и для группирования и структурирования самой RE. В сложных RE становится трудно отслеживать номера групп. Существует две функции, которые помогают решить эту проблему. Оба они используют общий синтаксис для расширений регулярных выражений, так что сначала посмотрим на это.

Perl 5 хорошо известен своими мощными дополнениями к стандартным регулярным выражениям. Для этих новых функций разработчики Perl не могли выбрать новые метасимволы с одним нажатием клавиши или новые специальные последовательности, начинающиеся с \, не делая регулярные выражения Perl запутанно отличающимися от стандартных RE. Например, если бы они выбрали & в качестве нового метасимвола, старые выражения предполагали бы, что & был обычным символ и не избежал бы его, написав \& или [&].

Решение, выбранное разработчиками Perl, заключалось в использовании (?...) в качестве синтаксиса расширения. ? сразу после круглой скобки была синтаксическая ошибка, потому что ? было бы нечего повторить, так что это не привело к каким-либо проблемам совместимости. Символы сразу же после ? указывают, какое расширение используется, так что (?=foo) - это одно (положительное утверждение в виде «lookhead»), а (?:foo) - что-то другое (нефиксирующая группа, содержащая подэкспрессию foo).

Python поддерживает несколько расширений Perl и добавляет синтаксис расширения к синтаксису расширения Perl. Если первая символ после вопросительного знака является P, вы знаете, что это расширение, которое является специфическим для Python.

Теперь, когда мы рассмотрели общий синтаксис расширения, мы можем вернуться к функциям, которые упрощают работу с группами в сложных RE.

Иногда вы хотите использовать группу для обозначения части регулярного выражения, но не заинтересованы в получении содержимого группы. Этот факт можно сделать явным, используя группу без захвата: (?:...), где можно заменить ... любым другим регулярным выражением.:

>>> m = re.match("([abc])+", "abc")
>>> m.groups()
('c',)
>>> m = re.match("(?:[abc])+", "abc")
>>> m.groups()
()

За исключением того факта, что вы не можете получить содержимое того, что группа согласована, группа без захвата ведет себя точно так же, как группа захвата; внутри него можно поместить что угодно, повторить его метасимволом повторения, таким как *, и вложить в другие группы (захватывающие или не захватывающие). (?:...) особенно полезно при изменении существующего шаблона, поскольку можно добавлять новые группы без изменения нумерации всех других групп. Следует отметить, что нет разницы в производительности поиска между группами захвата и не захвата; ни одна из форм не является более быстрой, чем другая.

Более значимой особенностью являются именованные группы: вместо ссылки на них по числам на группы можно ссылаться по имени.

Синтаксис именованной группы - это одно из специфичных для Python расширений: (?P<name>...). name, очевидно, название группы. Именованные группы ведут себя точно так же, как захватывающие группы, и дополнительно связывают имя с группой. Те объект соответствия методы, которые занимаются захватом групп, все принимают либо целые числа, которые ссылаются на группу по номеру, либо строки, которые содержат имя нужной группы. Именованным группам по-прежнему присваиваются номера, поэтому получить информацию о группе можно двумя способами:

>>> p = re.compile(r'(?P<word>\b\w+\b)')
>>> m = p.search( '(((( Lots of punctuation )))' )
>>> m.group('word')
'Lots'
>>> m.group(1)
'Lots'

Кроме того, можно извлечь именованные группы в виде словаря с помощью groupdict():

>>> m = re.match(r'(?P<first>\w+) (?P<last>\w+)', 'Jane Doe')
>>> m.groupdict()
{'first': 'Jane', 'last': 'Doe'}

Именованные группы удобны, потому что позволяют использовать легко запоминаемые имена, вместо необходимости запоминать цифры. Вот пример RE из модуля imaplib:

InternalDate = re.compile(r'INTERNALDATE "'
        r'(?P<day>[ 123][0-9])-(?P<mon>[A-Z][a-z][a-z])-'
        r'(?P<year>[0-9][0-9][0-9][0-9])'
        r' (?P<hour>[0-9][0-9]):(?P<min>[0-9][0-9]):(?P<sec>[0-9][0-9])'
        r' (?P<zonen>[-+])(?P<zoneh>[0-9][0-9])(?P<zonem>[0-9][0-9])'
        r'"')

Очевидно, что гораздо проще извлечь m.group('zonem'), вместо того, чтобы помнить, чтобы извлечь группу 9.

Синтаксис для обратных ссылок в выражении, таком как (...)\1, относится к номеру группы. Естественно, существует вариант, в котором вместо числа используется имя группы. Это еще одно расширение Python: (?P=name) указывает, что содержимое вызываемой группы name должно быть вновь сопоставлено в текущей точке. Регулярное выражение для поиска удвоенных слов, \b(\w+)\s+\1\b также может быть записано как \b(?P<word>\w+)\s+(?P=word)\b:

>>> p = re.compile(r'\b(?P<word>\w+)\s+(?P=word)\b')
>>> p.search('Paris in the the spring').group()
'the the'

Предварительные утверждения

Еще одним утверждением нулевой ширины является утверждение «поиск». Утверждения Lookahead доступны как в положительной, так и в отрицательной форме, и выглядят так:

(?=...) положительное утверждение вида. Это успешно, если содержащееся регулярное выражение, представленное здесь как ..., успешно совпадает с текущим расположением и в противном случае не удается. Но, как только сдержанное выражение было испробовано, механизм сопоставления не продвигается совсем; остальная часть шаблона пробуется прямо там, где началось утверждение.

(?!...) отрицательное утверждение вида. Это противоположно положительному утверждению; успешно, если содержащееся в нем выражение * не * совпадает с текущей позицией в строка.

Чтобы сделать это конкретным, давайте посмотрим на случай, где поиск полезно. Рассмотрим простой шаблон для сопоставления имени файла и разделения его на базовое имя и расширение, разделенные .. Например, в news.rc, news - базовое имя, а rc - расширение имени файла.

Шаблон для соответствия этому довольно прост:

.*[.].*$

Обратите внимание, что . нужно обрабатывать специально, потому что это метасимвол, поэтому он внутри символ класс, чтобы соответствовать только этому конкретному символ. Также обратите внимание на заднюю $; это добавляется для обеспечения того, чтобы все остальные строка были включены в расширение. Это регулярное выражение соответствует foo.bar и autoexec.bat и sendmail.cf и printers.conf.

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

.*[.][^b].*$ первая попытка выше пытается исключить bat, требуя, чтобы первый символ расширения не был b. Это неправильно, потому что шаблон также не совпадает с foo.bar.

.*[.]([^b]..|.[^a].|..[^t])$

Выражение становится мессье, когда вы пытаетесь исправить первое решение, требуя, чтобы один из следующих случаев совпадал: первый символ расширения не является b; второй символ не является a; или третий символ не является t. Он принимает foo.bar и отклоняет autoexec.bat, но требует трехбуквенного расширения и не принимает имя файла с двухбуквенным расширением, таким как sendmail.cf. Мы снова усложним схему, чтобы исправить ее.

.*[.]([^b].?.?|.[^a]?.?|..?[^t]?)$

В третьей попытке вторая и третья буквы становятся необязательными, чтобы обеспечить совпадение расширений короче трех символов, таких как sendmail.cf.

Модель становится очень сложной, что затрудняет чтение и понимание. Хуже того, если проблема изменится и вы хотите исключить как bat, так и exe как расширения, шаблон станет еще более сложным и запутанным.

Негативный взгляд прорезает всю эту путаницу:

.*[.](?!bat$)[^.]*$ отрицательный взгляд означает: если выражение bat не совпадает в этой точке, попробуйте остальную часть шаблона; если bat$ совпадает, то весь шаблон не будет выполнен. Перемещение $ требуется, чтобы гарантировать, что что-то как sample.batch, где расширение только начинается с bat, будет позволено. Команда [^.]* обеспечивает работу шаблона при наличии нескольких точек в имени файла.

Исключение другого расширения имени файла теперь легко; просто добавить его в качестве альтернативы внутри утверждения. Следующий шаблон исключает имена файлов, которые заканчиваются как bat, так и exe:

.*[.](?!bat$|exe$)[^.]*$

Изменение строк

До этого момента мы просто выполняли поиск статического строка. Регулярные выражения - также обычно используемый, чтобы изменить строки различными способами, используя следующий образец methods:

Метод/Атрибут Цель
split() Разделить строку на список, разбив её там, где есть соответствие RE
sub() Найдите все подстроки, где RE совпадает и заменить их другой строкой
subn() Делает то же самое, что и sub(), но возвращает новую строку и количество замен

Разделение строк

split() метод шаблона разделяет строка на части, где совпадает RE, возвращая список частей. Это похоже на split() метод строки, но обеспечивает гораздо более общую в разделителях, на которые можно разделить; строка split() поддерживает только разделение по пробелам или фиксированным строка. Как вы ожидали бы, есть функция уровня модуля re.split(), также.

.split(string[, maxsplit=0])

Разделить string по совпадениям регулярного выражения. Если в RE используемый скобки, то их содержимое также будет возвращено как часть результирующего списка. Если maxsplit является ненулевым, выполняются не более maxsplit разбиений.

Можно ограничить количество произведенных разбиений, передав значение для параметра maxsplit. Когда maxsplit не равен нулю, будет выполнено максимум разбиений maxsplit, а остаток строка возвращается в качестве окончательного элемента списка. В следующем примере разделителем является любая последовательность не алфавитно-цифровых символов:

>>> p = re.compile(r'\W+')
>>> p.split('This is a test, short and sweet, of split().')
['This', 'is', 'a', 'test', 'short', 'and', 'sweet', 'of', 'split', '']
>>> p.split('This is a test, short and sweet, of split().', 3)
['This', 'is', 'a', 'test, short and sweet, of split().']

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

>>> p = re.compile(r'\W+')
>>> p2 = re.compile(r'(\W+)')
>>> p.split('This... is a test.')
['This', 'is', 'a', 'test', '']
>>> p2.split('This... is a test.')
['This', '... ', 'is', ' ', 'a', ' ', 'test', '.', '']

Функция уровня модуля re.split() добавляет RE, чтобы быть используемый в качестве первого аргумента, но в противном случае является той же самой.:

>>> re.split(r'[\W]+', 'Words, words, words.')
['Words', 'words', 'words', '']
>>> re.split(r'([\W]+)', 'Words, words, words.')
['Words', ', ', 'words', ', ', 'words', '.', '']
>>> re.split(r'[\W]+', 'Words, words, words.', 1)
['Words', 'words, words.']

Поиск и замена

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

.sub(replacement, string[, count=0])

Возвращает строка, полученный заменой крайних левых неперекрывающихся вхождений RE в string заменяющей replacement. Если шаблон не найден, string возвращается без изменений.

Необязательный аргумент count - максимальное число заменяемых экземпляров шаблона; count должно быть неотрицательным целым числом. Значение по умолчанию 0 означает замену всех вхождений.

Вот простой пример использования sub() метод. Он заменяет названия цветов словом colour:

>>> p = re.compile('(blue|white|red)')
>>> p.sub('colour', 'blue socks and red shoes')
'colour socks and colour shoes'
>>> p.sub('colour', 'blue socks and red shoes', count=1)
'colour socks and red shoes'

Команда subn() метод выполняет ту же работу, но возвращает 2-кортеж, содержащий новое значение строка и число произведенных замен:

>>> p = re.compile('(blue|white|red)')
>>> p.subn('colour', 'blue socks and red shoes')
('colour socks and colour shoes', 2)
>>> p.subn('colour', 'no colours at all')
('no colours at all', 0)

Пустые совпадения заменяются только в том случае, если они не соседствуют с предыдущим пустым совпадением.:

>>> p = re.compile('x*')
>>> p.sub('-', 'abxd')
'-a-b--d-'

Если replacement является строкой, то в ней обрабатываются все сбеги обратной косой черты. То есть \n преобразуется в одиночный символ новой строки, \r преобразуется в возврат каретки и так далее. Неизвестные побеги, такие как \&, остаются одни. Обратные ссылки, такие как \6, заменяются подстрокой, соответствующей соответствующей группе в RE. Это позволяет включать части исходного текста в результирующую строка замены.

Этот пример соответствует слову section, за которым следует строка, заключенный в {, } и изменения section в subsection:

>>> p = re.compile('section{ ( [^}]* ) }', re.VERBOSE)
>>> p.sub(r'subsection{\1}','section{First} section{second}')
'subsection{First} subsection{second}'

Существует также синтаксис для ссылки на именованные группы, определенные синтаксисом (?P<name>...). \g<name> будет использовать подстроку, соответствующую группе с именем name, а \g<number> - соответствующий номер группы. Поэтому \g<2> эквивалентен \2, но не является неоднозначным в замещающем строка, таком как \g<2>0. (\20 интерпретировался бы как ссылка на группу 20, не ссылка на группу 2, сопровождаемую литерал символ '0'.) следующие замены все эквивалентны, но используют все три варианта замены строка:

>>> p = re.compile('section{ (?P<name> [^}]* ) }', re.VERBOSE)
>>> p.sub(r'subsection{\1}','section{First}')
'subsection{First}'
>>> p.sub(r'subsection{\g<1>}','section{First}')
'subsection{First}'
>>> p.sub(r'subsection{\g<name>}','section{First}')
'subsection{First}'

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

В следующем примере функция замены переводит десятичные разряды в шестнадцатеричные:

>>> def hexrepl(match):
...     "Return the hex string for a decimal number"
...     value = int(match.group())
...     return hex(value)
...
>>> p = re.compile(r'\d+')
>>> p.sub(hexrepl, 'Call 65490 for printing, 49152 for user code.')
'Call 0xffd2 for printing, 0xc000 for user code.'

Используя функцию уровня модуля re.sub(), образец передан как первый аргумент. Шаблон может быть предоставлен как объект или как строка; если необходимо указать флаги регулярного выражения, необходимо либо использовать объект шаблона в качестве первого параметра, либо использовать встроенные модификаторы в строке шаблона, например, sub("(?i)b+", "x", "bbbb BBBB") возвращает 'x x'.

Общие проблемы

Регулярные выражения являются мощным инструментом для некоторых приложений, но в некоторых отношениях их поведение не интуитивно, и иногда они не ведут себя так, как вы можете ожидать. В этом разделе будут указаны некоторые из наиболее распространенных подводных камней.

Использование методов строки

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

Одним из примеров может быть замена одного фиксированного строка другим; например, можно заменить word на deed. re.sub() похоже на функцию, используемую для этого, но рассмотрим replace() метод. Обратите внимание, что replace() также заменит word внутри слов, превращая swordfish в sdeedfish, но наивные RE word сделали бы это тоже. (Чтобы избежать замены частей слов, шаблон должен быть \bword\b, чтобы требовать, чтобы у word была граница слова с любой стороны. Это выходит за рамки возможностей replace().)

Другой распространенной задачей является удаление каждого вхождения одного символ из строка или замена его другим отдельным символ. Вы можете сделать это с чем-то вроде re.sub('\n', ' ', S), но translate() способен выполнять обе задачи и будет быстрее, чем любая операция регулярного выражения может быть.

Короче говоря, прежде, чем обратиться к модулю re, рассмотрите, может ли ваша проблема быть решена с более быстрым и более простым строковым метод.

Жадный против нежадного

При повторении регулярного выражения, как в a*, результирующее действие состоит в том, чтобы потреблять как можно больше образца. Этот факт часто укусывает вас, когда вы пытаетесь сопоставить пару сбалансированных разделителей, таких как угловые скобки, окружающие HTML-тег. Наивный шаблон для сопоставления одного HTML-тега не работает из-за жадности .*.:

>>> s = '<html><head><title>Title</title>'
>>> len(s)
32
>>> print(re.match('<.*>', s).span())
(0, 32)
>>> print(re.match('<.*>', s).group())
<html><head><title>Title</title>

RE соответствует '<' в '<html>', а .* потребляет остальную часть строка. Там еще более оставлен в RE, тем не менее, и > не может соответствовать в конце последовательности, таким образом, двигатель регулярного выражения должен возвратиться символ символ, пока это не находит достойным >. Финальный матч простирается от '<' в '<html>' до '>' в '</title>', что не то, что вы хотите.

В этом случае решение состоит в том, чтобы использовать нежадные определители *?, +?, ?? или {m,n}?, которые соответствуют максимально тексту little. В вышеприведенном примере '>' пробуется сразу же после совпадения первых '<', и когда он выходит из строя, двигатель продвигает символ за раз, повторяя '>' на каждом шаге. Это дает правильный результат:

>>> print(re.match('<.*?>', s).group())
<html>

(Обратите внимание, что парсинг HTML или XML с регулярными выражениями является болезненным. Быстрые и грязные шаблоны будут обрабатывать обычные случаи, но HTML и XML имеют особые случаи, которые нарушат очевидное регулярное выражение; к тому времени, когда вы напишете регулярное выражение, которое обрабатывает все возможные случаи, шаблоны будут очень сложными. Используйте HTML или модуль XML парсер для таких задач.)

Использование re.VERBOSE

К настоящему времени вы, вероятно, заметили, что регулярные выражения являются очень компактной нотацией, но они не являются сильно читаемыми. RE умеренной сложности могут стать длинными коллекциями обратных косых знаков, скобок и метасимволов, что затрудняет их чтение и понимание.

Для таких RE может быть полезно указать флаг re.VERBOSE при компиляции регулярного выражения, поскольку он позволяет более четко форматировать регулярное выражение.

Флаг re.VERBOSE имеет несколько эффектов. Пустое пространство в регулярном выражении, которое * не находится * внутри символ класс, игнорируется. Это означает, что выражение, такое как dog | cat, эквивалентно менее читаемому dog|cat, но [a b] по-прежнему будет соответствовать символам 'a', 'b' или пробелу. Кроме того, вы также можете поместить комментарии в RE; комментарии простираются от # символ до следующей новой строки. Когда используемый с трижды указанными последовательностями, это включает REs быть отформатированным более аккуратно:

pat = re.compile(r"""
 \s*                 # Пропустить ведущие пробелы
 (?P<header>[^:]+)   # Имя заголовка
 \s* :               # Пробел и двоеточие
 (?P<value>.*?)      # Значение заголовка - *? используется для
                     # потери следующего конечного пробела
 \s*$                # Конечный пробел до конца строки
""", re.VERBOSE)

Это гораздо более читаемо, чем:

pat = re.compile(r"\s*(?P<header>[^:]+)\s*:(?P<value>.*?)\s*$")

Обратная связь

Регулярные выражения - сложная тема. Этот документ помог вам понять их? были ли части, которые были неясны, или проблемы, с которыми вы столкнулись, не были покрыты здесь? если это так, пожалуйста, отправьте предложения по улучшению автору.

Наиболее полной книгой о регулярных выражениях почти наверняка является книга Джеффри Фридла «Регулярные Выражения», изданная o’Reilly. К сожалению, он концентрируется исключительно на Perl и Java диалекты регулярных выражений, и вообще не содержит никаких Python материала, поэтому он не будет полезен в качестве справочного для программирования в Python. (Первый выпуск касался Python’s теперь удаленный модуль regex, который не поможет вам очень.) Рассмотрите возможность извлечения из вашей библиотеки.