regex.md
1> **Источник:** https://python-all.ru/3.4/howto/regex.html2>3> «Документация Python на русском» – неофициальный перевод официальной документации Python: версии от 2.6 до 3.16, полнотекстовый поиск, английский оригинал рядом с переводом. Эта Markdown-версия страницы предназначена для работы с LLM: вставьте её в ChatGPT, Claude или Cursor.45---67# Регулярные выражения: руководство89| Автор: | A.M. Kuchling \<[amk@amk.ca](https://python-all.ru/3.4/howto/regex.html)\> |10| --- | --- |1112Аннотация1314Этот документ представляет собой вводное руководство по использованию регулярных выражений в Python с модулем [`re`](https://python-all.ru/3.4/library/re.html#module-re). Он даёт более мягкое введение, чем соответствующий раздел в справочнике библиотеки.1516## Введение1718Регулярные выражения (называемые RE, regex или regex-шаблоны) – это по сути крошечный, узкоспециализированный язык программирования, встроенный в Python и доступный через модуль [`re`](https://python-all.ru/3.4/library/re.html#module-re). С помощью этого маленького языка задаются правила для множества возможных строк, которые требуется сопоставить; это множество может содержать английские предложения, адреса электронной почты, команды TeX или что угодно. Затем можно задавать вопросы: «Соответствует ли эта строка шаблону?», или «Есть ли совпадение с шаблоном где-нибудь в этой строке?». Также можно использовать RE для изменения строки или её разделения различными способами.1920Шаблоны регулярных выражений компилируются в набор байткодов, которые затем выполняются механизмом сопоставления, написанным на C. Для продвинутого использования может потребоваться тщательно следить за тем, как механизм будет выполнять данное RE, и писать RE определённым образом, чтобы получить байткод, работающий быстрее. Оптимизация не рассматривается в этом документе, поскольку она требует хорошего понимания внутреннего устройства механизма сопоставления.2122Язык регулярных выражений относительно мал и ограничен, поэтому не все задачи обработки строк можно решить с помощью регулярных выражений. Существуют также задачи, которые *можно* выполнить с помощью регулярных выражений, но выражения оказываются очень сложными. В таких случаях, возможно, лучше написать код на Python для обработки; хотя код на Python будет медленнее, чем сложное регулярное выражение, он, вероятно, будет более понятным.2324## Простые шаблоны2526Начнём с изучения самых простых регулярных выражений. Поскольку регулярные выражения используются для работы со строками, начнём с самой распространённой задачи: сопоставления символов.2728Подробное объяснение теоретических основ регулярных выражений (детерминированные и недетерминированные конечные автоматы) можно найти почти в любом учебнике по написанию компиляторов.2930### Сопоставление символов3132Большинство букв и символов просто совпадают сами с собой. Например, регулярное выражение `test` точно совпадёт со строкой `test`. (Можно включить режим без учёта регистра, который позволит этому RE совпадать с `Test` или `TEST` ; подробнее об этом позже.)3334Из этого правила есть исключения: некоторые символы являются специальными *метасимволами* и не сопоставляются сами с собой. Вместо этого они указывают, что должно быть сопоставлено что-то необычное, или влияют на другие части РВ, повторяя их или меняя их значение. Значительная часть этого документа посвящена обсуждению различных метасимволов и их действий.3536Вот полный список метасимволов; их значения будут рассмотрены в остальной части этого HOWTO.3738```python39. ^ $ * + ? { } [ ] \ | ( )40```4142Первые метасимволы, которые мы рассмотрим, – это `[` и `]`. Они используются для указания класса символов – набора символов, которые требуется сопоставить. Символы можно перечислить по отдельности, или же диапазон символов можно указать, задав два символа и разделив их `'-'`. Например, `[abc]` совпадёт с любым из символов `a`, `b` или `c`; это то же самое, что `[a-c]`, где используется диапазон для задания того же набора символов. Если требуется сопоставить только строчные буквы, RE будет `[a-z]`.4344Внутри классов метасимволы не активны. Например, `[akm$]` совпадёт с любым из символов `'a'`, `'k'`, `'m'` или `'$'`; обычно `'$'` является метасимволом, но внутри класса символов он теряет свою особую природу.4546Вы можете сопоставить символы, не перечисленные в классе, *дополнив* набор. Это обозначается включением `'^'` в качестве первого символа класса; `'^'` вне символьного класса просто соответствует `'^'` символу. Например, `[^5]` будет соответствовать любому символу, кроме `'5'`.4748Пожалуй, самый важный метасимвол – обратная косая черта, `\`. Как и в строковых литералах Python, за обратной косой чертой могут следовать различные символы для обозначения специальных последовательностей. Она также используется для экранирования всех метасимволов, чтобы их можно было сопоставлять в шаблонах; например, если нужно сопоставить `[` или `\`, можно поставить перед ними обратную косую черту, чтобы убрать их специальное значение: `\[` или `\\`.4950Некоторые специальные последовательности, начинающиеся с `'\'`, представляют предопределённые наборы символов, которые часто бывают полезны: например, набор цифр, набор букв или набор всего, что не является пробельным символом.5152Рассмотрим пример: `\w` совпадает с любым буквенно-цифровым символом. Если шаблон регулярного выражения задан в байтах, это эквивалентно классу `[a-zA-Z0-9_]`. Если шаблон – строка, `\w` будет совпадать со всеми символами, помеченными как буквы в базе данных Unicode, предоставляемой модулем [`unicodedata`](https://python-all.ru/3.4/library/unicodedata.html#module-unicodedata). Можно использовать более ограниченное определение `\w` в строковом шаблоне, указав флаг [`re.ASCII`](https://python-all.ru/3.4/library/re.html#re.ASCII) при компиляции регулярного выражения.5354Следующий список специальных последовательностей неполон. Полный список последовательностей и расширенных определений классов для строковых шаблонов Unicode см. в последней части [*Синтаксис регулярных выражений*](https://python-all.ru/3.4/library/re.html#re-syntax) в справочнике стандартной библиотеки. В общем, версии Unicode соответствуют любому символу, находящемуся в соответствующей категории в базе данных Unicode.5556**`\d`**5758Соответствует любой десятичной цифре; эквивалентно классу5960`[0-9]`6162.6364**`\D`**6566Соответствует любому нецифровому символу; эквивалентно классу6768`[^0-9]`6970.7172**`\s`**7374Совпадает с любым пробельным символом; это эквивалентно классу7576`[ \t\n\r\f\v]`7778.7980**`\S`**8182Совпадает с любым непробельным символом; это эквивалентно классу8384`[^ \t\n\r\f\v]`8586.8788**`\w`**8990Совпадает с любым буквенно-цифровым символом; это эквивалентно классу9192`[a-zA-Z0-9_]`9394.9596**`\W`**9798Совпадает с любым не буквенно-цифровым символом; это эквивалентно классу99100`[^a-zA-Z0-9_]`101102.103104Эти последовательности можно включать внутрь класса символов. Например, `[\s,.]` – это класс символов, который совпадёт с любым пробельным символом, или `','`, или `'.'`.105106Последний метасимвол в этом разделе – `.`. Он совпадает с любым символом, кроме символа новой строки, и существует альтернативный режим (`re.DOTALL`), в котором он будет совпадать даже с символом новой строки. `'.'` часто используется, когда требуется сопоставить «любой символ».107108### Повторяющиеся элементы109110Возможность сопоставлять различные наборы символов – это первое, что регулярные выражения умеют делать, чего уже нельзя сделать с помощью методов строк. Однако если бы это была единственная дополнительная возможность регулярных выражений, они не были бы большим достижением. Ещё одна возможность – указывать, что части РВ должны повторяться определённое количество раз.111112Первый метасимвол для повторения, который мы рассмотрим, – это `*`. `*` не соответствует литеральному символу `*`; вместо этого он указывает, что предыдущий символ может быть сопоставлен ноль или более раз, а не ровно один раз.113114Например, `ca*t` совпадёт с `ct` (0 символов `a`), `cat` (1 `a`), `caaat` (3 `a` символа) и так далее. Движок RE имеет различные внутренние ограничения, связанные с размером типа `int` в языке C, которые не позволяют ему совпадать с более чем 2 миллиардами символов `a`; шаблоны обычно не пишутся для сопоставления с таким объёмом данных.115116Повторы, такие как `*`, являются *жадными*; при повторении RE движок сопоставления будет пытаться повторить его максимально возможное количество раз. Если последующие части шаблона не совпадают, движок сопоставления отступает назад и пробует снова с меньшим числом повторов.117118Пошаговый пример прояснит это. Рассмотрим выражение `a[bcd]*b`. Оно совпадает с буквой `'a'`, нулём или более букв из класса `[bcd]` и, наконец, заканчивается `'b'`. Представим сопоставление этого RE со строкой `abcbd`.119120| Шаг | Совпадение | Пояснение |121| --- | --- | --- |122| 1 | `a` | `a` в RE совпадает. |123| 2 | `abcbd` | Движок сопоставляет `[bcd]*`, заходя так далеко, как может, то есть до конца строки. |124| 3 | *Неудача* | Движок пытается сопоставить `b`, но текущая позиция находится в конце строки, поэтому это не удаётся. |125| 4 | `abcb` | Отступить, чтобы `[bcd]*` совпадал на один символ меньше. |126| 5 | *Неудача* | Попробовать `b` снова, но текущая позиция находится на последнем символе, которым является `'d'`. |127| 6 | `abc` | Снова отступить, чтобы `[bcd]*` совпадал только с `bc`. |128| 6 | `abcb` | Попробуйте `b` снова. На этот раз символ в текущей позиции – `'b'`, поэтому он совпадает. |129130Достигнут конец РВ, и оно совпало с `abcb`. Это показывает, как движок сопоставления сначала проходит максимально далеко, и если совпадение не найдено, он постепенно отступает и снова и снова пробует остаток РВ. Он будет отступать, пока не попробует ноль совпадений для `[bcd]*`, и если это в итоге не удаётся, движок делает вывод, что строка вообще не соответствует РВ.131132Ещё один повторяющийся метасимвол – `+`, который совпадает один или более раз. Обратите внимание на разницу между `*` и `+`; `*` совпадает *ноль* или более раз, поэтому повторяемый элемент может вообще отсутствовать, в то время как `+` требует как минимум *одного* вхождения. Для аналогичного примера, `ca+t` совпадёт с `cat` (1 `a`), `caaat` (3 `a`), но не совпадёт с `ct`.133134Есть ещё два повторяющихся квантификатора. Символ вопросительного знака, `?`, совпадает один раз или ноль раз; можно считать, что он помечает что-то как необязательное. Например, `home-?brew` совпадёт либо с `homebrew`, либо с `home-brew`.135136Самый сложный повторяющийся квантификатор – `{m,n}`, где *m* и *n* – десятичные целые числа. Этот квантификатор означает, что должно быть как минимум *m* повторений и не более *n*. Например, `a/{1,3}b` совпадёт с `a/b`, `a//b` и `a///b`. Он не совпадёт с `ab` (без слэшей) или с `a////b` (четыре слэша).137138Можно опустить либо *m*, либо *n*; в этом случае подразумевается разумное значение для отсутствующего параметра. Пропуск *m* интерпретируется как нижняя граница 0, а пропуск *n* приводит к верхней границе бесконечности – на самом деле верхняя граница – это упомянутый ранее предел в 2 миллиарда, но её можно считать бесконечностью.139140Можно заметить, что три остальных квантификатора можно выразить через эту нотацию. `{0,}` – то же, что `*`, `{1,}` эквивалентен `+`, а `{0,1}` – то же, что `?`. Лучше использовать `*`, `+` или `?`, когда это возможно, просто потому что они короче и легче читаются.141142## Использование регулярных выражений143144Теперь, когда мы рассмотрели несколько простых регулярных выражений, как же их использовать в Python? Модуль [`re`](https://python-all.ru/3.4/library/re.html#module-re) предоставляет интерфейс к движку регулярных выражений, позволяя компилировать РВ в объекты и затем выполнять с ними сопоставление.145146### Компиляция регулярных выражений147148Регулярные выражения компилируются в объекты шаблонов, которые имеют методы для различных операций, таких как поиск совпадений с шаблоном или выполнение подстановок в строке.149150```python151>>> import re152>>> p = re.compile('ab*')153>>> p154re.compile('ab*')155```156157[`re.compile()`](https://python-all.ru/3.4/library/re.html#re.compile) также принимает необязательный аргумент *flags*, используемый для включения различных специальных возможностей и синтаксических вариаций. Мы рассмотрим доступные настройки позже, а пока достаточно одного примера:158159```python160>>> p = re.compile('ab*', re.IGNORECASE)161```162163РВ передаётся в [`re.compile()`](https://python-all.ru/3.4/library/re.html#re.compile) в виде строки. РВ обрабатываются как строки, потому что регулярные выражения не являются частью основного языка Python, и для их записи не было создано специального синтаксиса. (Есть приложения, которым РВ вообще не нужны, так что нет необходимости раздувать спецификацию языка, включая их.) Вместо этого модуль [`re`](https://python-all.ru/3.4/library/re.html#module-re) – это просто модуль-расширение на C, входящий в состав Python, как и модули [`socket`](https://python-all.ru/3.4/library/socket.html#module-socket) или [`zlib`](https://python-all.ru/3.4/library/zlib.html#module-zlib).164165Размещение RE в строках сохраняет язык Python более простым, но имеет один недостаток, который является темой следующего раздела.166167### Проблема обратной косой черты168169Как было сказано ранее, регулярные выражения используют символ обратной косой черты (`'\'`) для обозначения специальных форм или для использования специальных символов без их специального значения. Это конфликтует с использованием того же символа в строковых литералах Python для той же цели.170171Допустим, нужно написать РВ, которое совпадает со строкой `\section`, которая может встречаться в файле LaTeX. Чтобы понять, что писать в коде программы, начните с нужной строки для сопоставления. Затем нужно экранировать все обратные слэши и другие метасимволы, предваряя их обратным слэшем, в результате получится строка `\\section`. Результирующая строка, которую нужно передать в [`re.compile()`](https://python-all.ru/3.4/library/re.html#re.compile), должна быть `\\section`. Однако, чтобы выразить это в виде строкового литерала Python, оба обратных слэша необходимо экранировать *снова*.172173| Символы | Этап |174| --- | --- |175| `\section` | Строка для совпадения |176| `\\section` | Экранированный обратный слэш для [`re.compile()`](https://python-all.ru/3.4/library/re.html#re.compile) |177| `"\\\\section"` | Экранированные обратные слеши для строкового литерала |178179Короче говоря, для сопоставления с литералом обратной косой черты нужно записать `'\\\\'` в качестве строки RE, потому что регулярное выражение должно быть `\\`, а каждый обратный слеш должен быть представлен как `\\` внутри обычного строкового литерала Python. В RE, где обратные слеши встречаются многократно, это приводит к множеству повторных слешей и делает итоговые строки трудными для понимания.180181Решение – использовать в Python сырые строки для регулярных выражений; обратные слэши не обрабатываются особым образом в строковом литерале с префиксом `'r'`, поэтому `r"\n"` – это двухсимвольная строка, содержащая `'\'` и `'n'`, в то время как `"\n"` – односимвольная строка, содержащая символ новой строки. Регулярные выражения часто записываются в коде Python с использованием этой нотации сырых строк.182183| Обычная строка | Сырая строка |184| --- | --- |185| `"ab*"` | `r"ab*"` |186| `"\\\\section"` | `r"\\section"` |187| `"\\w+\\s+\\1"` | `r"\w+\s+\1"` |188189### Выполнение поиска совпадений190191Когда у вас есть объект, представляющий скомпилированное регулярное выражение, что с ним делать? Объекты-шаблоны имеют несколько методов и атрибутов. Здесь будут рассмотрены только самые важные; обратитесь к документации [`re`](https://python-all.ru/3.4/library/re.html#module-re) за полным списком.192193| Метод/Атрибут | Назначение |194| --- | --- |195| `match()` | Определяет, совпадает ли RE в начале строки. |196| `search()` | Сканирует строку в поисках любого места, где данное регулярное выражение совпадает. |197| `findall()` | Находит все подстроки, совпадающие с регулярным выражением, и возвращает их в виде списка. |198| `finditer()` | Find all substrings where the RE matches, and returns them as an [*iterator*](https://python-all.ru/3.4/glossary.html#term-iterator). |199200[`match()`](https://python-all.ru/3.4/library/re.html#re.regex.match) и [`search()`](https://python-all.ru/3.4/library/re.html#re.regex.search) возвращают `None`, если совпадение не найдено. Если они успешны, возвращается экземпляр [*объекта совпадения*](https://python-all.ru/3.4/library/re.html#match-objects), содержащий информацию о совпадении: где оно начинается и заканчивается, подстроку, с которой совпало, и другое.201202Вы можете узнать об этом, интерактивно экспериментируя с модулем [`re`](https://python-all.ru/3.4/library/re.html#module-re). Если у вас есть [`tkinter`](https://python-all.ru/3.4/library/tkinter.html#module-tkinter), возможно, вы также захотите посмотреть на [Tools/demo/redemo.py](https://python-all.ru/src/3.4/Tools/demo/redemo.py) – демонстрационную программу, входящую в состав дистрибутива Python. Она позволяет вводить РВ и строки и отображает, совпадает РВ или нет. `redemo.py` может быть весьма полезна при отладке сложного РВ. Phil Schwartz's [Kodos](https://python-all.ru/3.4/howto/regex.html) – также интерактивный инструмент для разработки и тестирования шаблонов РВ.203204В этом HOWTO используется стандартный интерпретатор Python для примеров. Сначала запустите интерпретатор Python, импортируйте модуль [`re`](https://python-all.ru/3.4/library/re.html#module-re) и скомпилируйте РВ:205206```python207>>> import re208>>> p = re.compile('[a-z]+')209>>> p210re.compile('[a-z]+')211```212213Теперь можно попробовать сопоставить различные строки с РВ `[a-z]+`. Пустая строка вообще не должна совпадать, так как `+` означает «одно или более повторений». `match()` в этом случае должна вернуть `None`, что приведёт к тому, что интерпретатор не выведет ничего. Можно явно напечатать результат `match()`, чтобы это стало очевидно.214215```python216>>> p.match("")217>>> print(p.match(""))218None219```220221Теперь попробуем на строке, которая должна совпасть, например `tempo`. В этом случае `match()` вернёт [*объект совпадения*](https://python-all.ru/3.4/library/re.html#match-objects), поэтому следует сохранить результат в переменной для дальнейшего использования.222223```python224>>> m = p.match('tempo')225>>> m 226<_sre.SRE_Match object; span=(0, 5), match='tempo'>227```228229Теперь можно запросить у [*объекта match*](https://python-all.ru/3.4/library/re.html#match-objects) информацию о соответствующей строке. [*Объекты match*](https://python-all.ru/3.4/library/re.html#match-objects) также имеют несколько методов и атрибутов; наиболее важные из них:230231| Метод/Атрибут | Назначение |232| --- | --- |233| `group()` | Возвращает строку, совпавшую с РВ |234| `start()` | Возвращает начальную позицию совпадения |235| `end()` | Возвращает конечную позицию совпадения |236| `span()` | Возвращает кортеж с (начало, конец) позициями совпадения |237238Применение этих методов быстро прояснит их смысл:239240```python241>>> m.group()242'tempo'243>>> m.start(), m.end()244(0, 5)245>>> m.span()246(0, 5)247```248249[`group()`](https://python-all.ru/3.4/library/re.html#re.match.group) возвращает подстроку, соответствующую регулярному выражению. [`start()`](https://python-all.ru/3.4/library/re.html#re.match.start) и [`end()`](https://python-all.ru/3.4/library/re.html#re.match.end) возвращают начальный и конечный индекс совпадения. [`span()`](https://python-all.ru/3.4/library/re.html#re.match.span) возвращает оба индекса в одном кортеже. Поскольку метод `match()` проверяет только совпадение с началом строки, `start()` всегда будет равен нулю. Однако метод `search()` у шаблонов сканирует строку целиком, так что в этом случае совпадение может начинаться не с нуля.250251```python252>>> print(p.match('::: message'))253None254>>> m = p.search('::: message'); print(m) 255<_sre.SRE_Match object; span=(4, 11), match='message'>256>>> m.group()257'message'258>>> m.span()259(4, 11)260```261262В реальных программах чаще всего [*объект совпадения*](https://python-all.ru/3.4/library/re.html#match-objects) сохраняют в переменную, а затем проверяют, не равен ли он `None`. Обычно это выглядит так:263264```python265p = re.compile( ... )266m = p.match( 'string goes here' )267if m:268 print('Match found: ', m.group())269else:270 print('No match')271```272273Два метода шаблона возвращают все совпадения для шаблона. [`findall()`](https://python-all.ru/3.4/library/re.html#re.regex.findall) возвращает список соответствующих строк:274275```python276>>> p = re.compile('\d+')277>>> p.findall('12 drummers drumming, 11 pipers piping, 10 lords a-leaping')278['12', '11', '10']279```280281`findall()` должен создать полный список, прежде чем вернуть его в качестве результата. Метод [`finditer()`](https://python-all.ru/3.4/library/re.html#re.regex.finditer) возвращает последовательность экземпляров [*объект совпадения*](https://python-all.ru/3.4/library/re.html#match-objects) в виде [*итератора*](https://python-all.ru/3.4/glossary.html#term-iterator):282283```python284>>> iterator = p.finditer('12 drummers drumming, 11 ... 10 ...')285>>> iterator 286<callable_iterator object at 0x...>287>>> for match in iterator:288... print(match.span())289...290(0, 2)291(22, 24)292(29, 31)293```294295### Функции уровня модуля296297You don’t have to create a pattern object and call its methods; the [`re`](https://python-all.ru/3.4/library/re.html#module-re) module also provides top-level functions called [`match()`](https://python-all.ru/3.4/library/re.html#re.match), [`search()`](https://python-all.ru/3.4/library/re.html#re.search), [`findall()`](https://python-all.ru/3.4/library/re.html#re.findall), [`sub()`](https://python-all.ru/3.4/library/re.html#re.sub), and so forth. These functions take the same arguments as the corresponding pattern method with the RE string added as the first argument, and still return either `None` or a [*match object*](https://python-all.ru/3.4/library/re.html#match-objects) instance.298299```python300>>> print(re.match(r'From\s+', 'Fromage amk'))301None302>>> re.match(r'From\s+', 'From amk Thu May 14 19:12:10 1998') 303<_sre.SRE_Match object; span=(0, 5), match='From '>304```305306Под капотом эти функции просто создают объект шаблона и вызывают на нём соответствующий метод. Они также сохраняют скомпилированный объект в кеш, так что последующие вызовы с тем же РВ не будут каждый раз разбирать шаблон заново.307308Следует ли использовать функции уровня модуля или получать шаблон и вызывать его методы самостоятельно? При доступе к регулярному выражению внутри цикла предварительная компиляция позволит сэкономить несколько вызовов функций. Вне циклов, благодаря внутреннему кешу, разница невелика.309310### Флаги компиляции311312Флаги компиляции позволяют изменять некоторые аспекты работы регулярных выражений. Флаги доступны в модуле [`re`](https://python-all.ru/3.4/library/re.html#module-re) под двумя именами: длинным, например `IGNORECASE`, и коротким, однобуквенным, например `I`. (Если вы знакомы с модификаторами шаблонов в Perl, то однобуквенные формы используют те же буквы; например, короткая форма [`re.VERBOSE`](https://python-all.ru/3.4/library/re.html#re.VERBOSE) – это [`re.X`](https://python-all.ru/3.4/library/re.html#re.X).) Несколько флагов можно задать с помощью побитового ИЛИ: например, `re.I | re.M` устанавливает оба флага `I` и `M`.313314Ниже приведена таблица доступных флагов, а затем более подробное описание каждого из них.315316| Флаг | Значение |317| --- | --- |318| `ASCII`, `A` | Заставляет несколько управляющих последовательностей, таких как `\\w`, `\\b`, `\\s` и `\\d`, сопоставляться только с ASCII-символами, обладающими соответствующим свойством. |319| `DOTALL`, `S` | Заставляет `.` соответствовать любому символу, включая символы новой строки |320| `IGNORECASE`, `I` | Выполняет регистронезависимые сопоставления |321| `LOCALE`, `L` | Выполняет сопоставление с учётом локали |322| `MULTILINE`, `M` | Многострочное сопоставление, затрагивающее `^` и `$` |323| `VERBOSE`, `X` (от ‘extended’) | Включает подробные (verbose) регулярные выражения, которые можно организовать более чисто и понятно. |324325#### `I`326327#### `IGNORECASE`328329Выполняет сопоставление без учёта регистра; классы символов и строковые литералы будут сопоставляться с буквами, игнорируя регистр. Например, `[A-Z]` будет также находить строчные буквы, а `Spam` будет соответствовать `Spam`, `spam` или `spAM`. Это приведение к нижнему регистру не учитывает текущую локаль; оно будет учитывать, если также установить флаг `LOCALE`.330331#### `L`332333#### `LOCALE`334335Делает `\\w`, `\\W`, `\\b` и `\\B` зависимыми от текущей локали вместо базы данных Unicode.336337Локали – это возможность библиотеки C, предназначенная для написания программ, учитывающих языковые различия. Например, при обработке французского текста может понадобиться написать `\\w+` для поиска слов, но `\\w` соответствует только классу символов `[A-Za-z]`; он не будет находить `'é'` или `'ç'`. Если система настроена правильно и выбрана французская локаль, некоторые функции C сообщат программе, что `'é'` также следует считать буквой. Установка флага `LOCALE` при компиляции регулярного выражения заставит результирующий скомпилированный объект использовать эти функции C для `\\w`; это медленнее, но позволяет `\\w+` находить французские слова так, как и ожидается.338339#### `M`340341#### `MULTILINE`342343(`^` и `$` пока не объяснены; они будут представлены в разделе [*Дополнительные метасимволы*](https://python-all.ru/3.4/howto/regex.html#more-metacharacters).)344345Обычно `^` сопоставляется только в начале строки, а `$` – только в конце строки и непосредственно перед символом новой строки (если он есть) в конце строки. Когда указан этот флаг, `^` сопоставляется в начале строки и в начале каждой строки внутри строки, сразу после каждого символа новой строки. Аналогично, метасимвол `$` сопоставляется в конце строки и в конце каждой строки (непосредственно перед каждым символом новой строки).346347#### `S`348349#### `DOTALL`350351Заставляет специальный символ `'.'` сопоставляться с любым символом, включая символ новой строки; без этого флага `'.'` будет сопоставляться с любым символом, *кроме* символа новой строки.352353#### `A`354355#### `ASCII`356357Заставляет `\\w`, `\\W`, `\\b`, `\\B`, `\\s` и `\\S` выполнять сопоставление только по ASCII вместо полного сопоставления Unicode. Это имеет смысл только для шаблонов Unicode и игнорируется для байтовых шаблонов.358359#### `X`360361#### `VERBOSE`362363Этот флаг позволяет писать более читаемые регулярные выражения, предоставляя больше гибкости в их форматировании. Когда этот флаг указан, пробельные символы внутри строки регулярного выражения игнорируются, за исключением случаев, когда пробел находится внутри символьного класса или перед ним стоит неэкранированная обратная косая черта; это позволяет более четко организовывать и расставлять отступы в регулярном выражении. Этот флаг также позволяет помещать комментарии внутри регулярного выражения, которые будут игнорироваться движком; комментарии обозначаются символом `'#'`, который не находится внутри символьного класса и не предваряется неэкранированной обратной косой чертой.364365Например, вот RE, использующий [`re.VERBOSE`](https://python-all.ru/3.4/library/re.html#re.VERBOSE); видите, насколько его легче читать?366367```python368charref = re.compile(r"""369 &[#] # Start of a numeric entity reference370 (371 0[0-7]+ # Octal form372 | [0-9]+ # Decimal form373 | x[0-9a-fA-F]+ # Hexadecimal form374 )375 ; # Trailing semicolon376""", re.VERBOSE)377```378379Без режима verbose регулярное выражение выглядело бы так:380381```python382charref = re.compile("&#(0[0-7]+"383 "|[0-9]+"384 "|x[0-9a-fA-F]+);")385```386387В приведённом выше примере использовалась автоматическая конкатенация строковых литералов Python для разбиения RE на более мелкие части, но её по-прежнему сложнее понять, чем версию с [`re.VERBOSE`](https://python-all.ru/3.4/library/re.html#re.VERBOSE).388389## Дополнительные возможности шаблонов390391До сих пор мы рассмотрели только часть возможностей регулярных выражений. В этом разделе мы рассмотрим некоторые новые метасимволы и способы использования групп для извлечения частей сопоставленного текста.392393### Дополнительные метасимволы394395Остались некоторые метасимволы, которые мы ещё не рассмотрели. Большинство из них будет рассмотрено в этом разделе.396397Некоторые из оставшихся метасимволов, которые предстоит обсудить, – это *проверки с нулевой шириной* (zero-width assertions). Они не заставляют движок продвигаться по строке; вместо этого они вообще не потребляют символы и просто успешно проходят или нет. Например, `\\b` – это проверка того, что текущая позиция находится на границе слова; позиция `\\b` вообще не меняется. Это означает, что проверки с нулевой шириной никогда не следует повторять, потому что если они один раз совпадают в данной позиции, то, очевидно, могут совпадать бесконечное количество раз.398399**`|`**400401Альтернатива, или оператор «или». Если A и B – регулярные выражения, то `A|B` будет соответствовать любой строке, которая соответствует либо `A`, либо `B`. `|` имеет очень низкий приоритет, чтобы разумно работать при альтернативе многозначных строк. `Crow|Servo` будет соответствовать либо `Crow`, либо `Servo`, а не `Cro`, `'w'` или `'S'`, и `ervo`.402403Чтобы сопоставить литерал `'|'`, используйте `\|` или заключите его в символьный класс, как в `[|]`.404405**`^`**406407Соответствует началу строк. Если не установлен флаг `MULTILINE`, будет соответствовать только началу всей строки. В режиме `MULTILINE` также соответствует сразу после каждого символа новой строки внутри строки.408409Например, если нужно найти слово `From` только в начале строки, следует использовать RE `^From`.410411```python412>>> print(re.search('^From', 'From Here to Eternity')) 413<_sre.SRE_Match object; span=(0, 4), match='From'>414>>> print(re.search('^From', 'Reciting From Memory'))415None416```417418**`$`**419420Совпадение в конце строки, которая определяется как конец строки или любая позиция, за которой следует символ перевода строки.421422```python423>>> print(re.search('}$', '{block}')) 424<_sre.SRE_Match object; span=(6, 7), match='}'>425>>> print(re.search('}$', '{block} '))426None427>>> print(re.search('}$', '{block}\n')) 428<_sre.SRE_Match object; span=(6, 7), match='}'>429```430431Чтобы сопоставить литерал `'$'`, используйте `\$` или заключите его в символьный класс, как в `[$]`.432433**`\A`**434435Совпадает только в начале строки. Если не используется режим436437`MULTILINE`438439,440441`\A`442443и444445`^`446447по сути одно и то же. В режиме448449`MULTILINE`450451они различаются:452453`\A`454455по-прежнему совпадает только в начале строки, а456457`^`458459может совпадать в любом месте строки после символа новой строки.460461**`\Z`**462463Совпадение только в конце строки.464465**`\b`**466467Граница слова. Это утверждение нулевой длины, которое совпадает только в начале или конце слова. Слово определяется как последовательность буквенно-цифровых символов, поэтому конец слова обозначается пробелом или не-буквенно-цифровым символом.468469Следующий пример находит `class` только когда это целое слово; не будет находить, когда оно является частью другого слова.470471```python472>>> p = re.compile(r'\bclass\b')473>>> print(p.search('no class at all')) 474<_sre.SRE_Match object; span=(3, 8), match='class'>475>>> print(p.search('the declassified algorithm'))476None477>>> print(p.search('one subclass is'))478None479```480481При использовании этой специальной последовательности нужно помнить о двух тонкостях. Первая: это самое серьёзное столкновение между строковыми литералами Python и последовательностями регулярных выражений. В строковых литералах Python `\b` – это символ backspace (ASCII 8). Если не используются сырые строки, Python преобразует `\b` в backspace, и регулярное выражение будет работать не так, как ожидается. Следующий пример выглядит так же, как предыдущее регулярное выражение, но перед строкой RE нет `'r'`.482483```python484>>> p = re.compile('\bclass\b')485>>> print(p.search('no class at all'))486None487>>> print(p.search('\b' + 'class' + '\b')) 488<_sre.SRE_Match object; span=(0, 7), match='\x08class\x08'>489```490491Внутри символьного класса, где эта проверка не нужна, `\b` представляет символ backspace (для совместимости со строковыми литералами Python).492493**`\B`**494495Ещё одна проверка с нулевой длиной; противоположность496497`\b`498499. Совпадает, когда текущая позиция не находится на границе слова.500501### Группировка502503Часто требуется получить больше информации, чем просто – совпало RE или нет. Регулярные выражения часто используются для разбора строк с помощью RE, разделённого на несколько подгрупп, каждая из которых соответствует интересующему компоненту. Например, строка заголовка RFC-822 разделена на имя заголовка и значение, разделённые `':'`, так:504505```python506From: author@example.com507User-Agent: Thunderbird 1.5.0.9 (X11/20061227)508MIME-Version: 1.0509To: editor@example.com510```511512Это можно обработать, написав регулярное выражение, которое сопоставляет всю строку заголовка, и имеет одну группу для имени заголовка и другую – для значения заголовка.513514Группы выделяются метасимволами `'('` и `')'`. `'('` и `')'` имеют примерно тот же смысл, что и в математических выражениях: они группируют содержащиеся в них выражения, и содержимое группы можно повторять с помощью квантификаторов, таких как `*`, `+`, `?` или `{m,n}`. Например, `(ab)*` найдёт ноль или более повторений `ab`.515516```python517>>> p = re.compile('(ab)*')518>>> print(p.match('ababababab').span())519(0, 10)520```521522Группы, обозначенные `'('`, `')'`, также захватывают начальный и конечный индексы совпавшего текста; их можно получить, передав аргумент в `group()`, `start()`, `end()` и `span()`. Нумерация групп начинается с 0. Группа 0 присутствует всегда; это всё RE, поэтому методы [*объекта совпадения*](https://python-all.ru/3.4/library/re.html#match-objects) по умолчанию принимают группу 0. Позже мы увидим, как указать группы, не захватывающие диапазон совпавшего текста.523524```python525>>> p = re.compile('(a)b')526>>> m = p.match('ab')527>>> m.group()528'ab'529>>> m.group(0)530'ab'531```532533Подгруппы нумеруются слева направо, начиная с 1. Группы могут быть вложенными; чтобы определить номер, нужно просто посчитать открывающие скобки, идя слева направо.534535```python536>>> p = re.compile('(a(b)c)d')537>>> m = p.match('abcd')538>>> m.group(0)539'abcd'540>>> m.group(1)541'abc'542>>> m.group(2)543'b'544```545546`group()` можно передать несколько номеров групп одновременно; в этом случае он вернёт кортеж с соответствующими значениями для этих групп.547548```python549>>> m.group(2,1,2)550('b', 'abc', 'b')551```552553Метод `groups()` возвращает кортеж, содержащий строки для всех подгрупп – от 1 до их общего количества.554555```python556>>> m.groups()557('abc', 'b')558```559560Обратные ссылки в шаблоне позволяют указать, что содержимое ранее захваченной группы должно также присутствовать в текущей позиции строки. Например, `\1` будет успешным, если точное содержимое группы 1 можно найти в текущей позиции, иначе – нет. Помните, что строковые литералы Python также используют обратную косую черту с числами для включения произвольных символов в строку, поэтому обязательно используйте сырую строку при включении обратных ссылок в RE.561562Например, следующее RE обнаруживает удвоенные слова в строке.563564```python565>>> p = re.compile(r'(\b\w+)\s+\1')566>>> p.search('Paris in the the spring').group()567'the the'568```569570Обратные ссылки такого рода редко бывают полезны при простом поиске в строке – существует мало текстовых форматов, которые повторяют данные таким образом, – но вскоре обнаруживается, что они *очень* полезны при выполнении строковых подстановок.571572### Незахватывающие и именованные группы573574Сложные регулярные выражения могут использовать много групп как для захвата интересующих подстрок, так и для группировки и структурирования самого выражения. В сложных RE становится трудно отслеживать номера групп. Существуют две возможности, которые помогают решить эту проблему. Обе используют общий синтаксис расширений регулярных выражений, поэтому сначала рассмотрим его.575576Perl 5 хорошо известен своими мощными дополнениями к стандартным регулярным выражениям. Для этих новых возможностей разработчики Perl не могли выбрать новые метасимволы из одного символа или новые специальные последовательности, начинающиеся с `\`, не сделав регулярные выражения Perl запутанно отличающимися от стандартных RE. Если бы они выбрали `&` в качестве нового метасимвола, например, старые выражения предполагали бы, что `&` – это обычный символ, и не экранировали бы его, записывая `\&` или `[&]`.577578Разработчики Perl выбрали `(?...)` в качестве синтаксиса расширений. `?` сразу после открывающей скобки было синтаксической ошибкой, потому что `?` не к чему было бы применять повтор, так что никаких проблем совместимости это не вызвало. Символы сразу после `?` указывают, какое расширение используется: например, `(?=foo)` – одно (положительная опережающая проверка), а `(?:foo)` – другое (незахватывающая группа с подвыражением `foo`).579580Python поддерживает несколько расширений Perl и добавляет свой синтаксис расширений к синтаксису Perl. Если после вопросительного знака первым символом идёт `P`, значит, это расширение, специфичное для Python.581582Теперь, когда мы рассмотрели общий синтаксис расширений, можно вернуться к возможностям, упрощающим работу с группами в сложных RE.583584Иногда требуется использовать группу для обозначения части регулярного выражения, но нет необходимости извлекать содержимое группы. Это можно явно указать с помощью незахватывающей группы: `(?:...)`, где `...` можно заменить любым другим регулярным выражением.585586```python587>>> m = re.match("([abc])+", "abc")588>>> m.groups()589('c',)590>>> m = re.match("(?:[abc])+", "abc")591>>> m.groups()592()593```594595За исключением того, что нельзя извлечь содержимое того, что сопоставила группа, незахватывающая группа ведёт себя точно так же, как захватывающая: в неё можно поместить что угодно, повторить её с помощью метасимвола повторения, такого как `*`, и вложить в другие группы (захватывающие или незахватывающие). `(?:...)` особенно полезен при изменении существующего шаблона, поскольку можно добавлять новые группы, не меняя нумерацию всех остальных групп. Стоит упомянуть, что нет разницы в производительности поиска между захватывающими и незахватывающими группами; ни одна из форм не быстрее другой.596597Более значимая возможность – именованные группы: вместо обращения по номерам группы можно ссылаться по имени.598599Синтаксис для именованной группы – одно из расширений Python: `(?P<name>...)`. *name* – это, очевидно, имя группы. Именованные группы ведут себя точно так же, как захватывающие, и дополнительно связывают имя с группой. Методы [*объекта совпадения*](https://python-all.ru/3.4/library/re.html#match-objects), работающие с захваченными группами, принимают как целые числа (номер группы), так и строки (имя группы). Именованные группы по-прежнему имеют номера, поэтому информацию о группе можно получить двумя способами:600601```python602>>> p = re.compile(r'(?P<word>\b\w+\b)')603>>> m = p.search( '(((( Lots of punctuation )))' )604>>> m.group('word')605'Lots'606>>> m.group(1)607'Lots'608```609610Именованные группы удобны, потому что позволяют использовать легко запоминающиеся имена вместо запоминания номеров. Вот пример регулярного выражения из модуля [`imaplib`](https://python-all.ru/3.4/library/imaplib.html#module-imaplib):611612```python613InternalDate = re.compile(r'INTERNALDATE "'614 r'(?P<day>[ 123][0-9])-(?P<mon>[A-Z][a-z][a-z])-'615 r'(?P<year>[0-9][0-9][0-9][0-9])'616 r' (?P<hour>[0-9][0-9]):(?P<min>[0-9][0-9]):(?P<sec>[0-9][0-9])'617 r' (?P<zonen>[-+])(?P<zoneh>[0-9][0-9])(?P<zonem>[0-9][0-9])'618 r'"')619```620621Очевидно, что получить `m.group('zonem')` гораздо проще, чем запоминать, что нужно получить группу 9.622623Синтаксис обратных ссылок в выражении, например `(...)\1`, относится к номеру группы. Существует вариант, использующий имя группы вместо номера. Это ещё одно расширение Python: `(?P=name)` означает, что содержимое группы с именем *name* должно снова совпадать в текущей позиции. Регулярное выражение для поиска повторяющихся слов, `(\b\w+)\s+\1`, можно также записать как `(?P<word>\b\w+)\s+(?P=word)`:624625```python626>>> p = re.compile(r'(?P<word>\b\w+)\s+(?P=word)')627>>> p.search('Paris in the the spring').group()628'the the'629```630631### Опережающие проверки632633Ещё одна проверка нулевой ширины – опережающая проверка. Опережающие проверки бывают положительными и отрицательными и выглядят так:634635**`(?=...)`**636637Положительная опережающая проверка. Она успешна, если вложенное регулярное выражение, представленное здесь как638639`...`640641, успешно совпадает в текущей позиции, и неуспешна в противном случае. Но как только вложенное выражение проверено, движок сопоставления не продвигается; остальная часть шаблона проверяется прямо с того места, где началась проверка.642643**`(?!...)`**644645Отрицательная опережающая проверка. Это противоположность положительной проверке; она успешна, если вложенное выражение646647*не*648649совпадает в текущей позиции строки.650651Чтобы стало понятнее, рассмотрим случай, где полезна опережающая проверка. Возьмём простой шаблон для сопоставления имени файла и разделения его на имя базы и расширение, разделённые точкой `.`. Например, в `news.rc` `news` – имя базы, а `rc` – расширение.652653Шаблон для сопоставления этого довольно прост:654655`.*[.].*$`656657Обратите внимание, что с точкой `.` нужно обращаться особым образом, так как это метасимвол; поэтому она помещена в символьный класс, чтобы совпадал только этот конкретный символ. Также обратите внимание на завершающую `$`: она добавлена, чтобы гарантировать включение в расширение всей оставшейся части строки. Это регулярное выражение находит `foo.bar`, `autoexec.bat`, `sendmail.cf` и `printers.conf`.658659Теперь усложним задачу: нужно найти имена файлов, у которых расширение не равно `bat`. Несколько неправильных попыток:660661`.*[.][^b].*$` Первая попытка пытается исключить `bat`, требуя, чтобы первый символ расширения не был `b`. Это неправильно, потому что шаблон также не находит `foo.bar`.662663`.*[.]([^b]..|.[^a].|..[^t])$`664665Выражение становится всё запутаннее, когда мы пытаемся исправить первое решение, требуя, чтобы выполнялся один из следующих случаев: первый символ расширения не равен `b`; второй символ не равен `a`; или третий символ не равен `t`. Такой подход принимает `foo.bar` и отвергает `autoexec.bat`, но требует трёхбуквенного расширения и не будет принимать имена с двухбуквенным расширением, например `sendmail.cf`. Мы снова усложним шаблон, пытаясь это исправить.666667`.*[.]([^b].?.?|.[^a]?.?|..?[^t]?)$`668669В третьей попытке вторая и третья буквы сделаны необязательными, чтобы допустить расширения короче трёх символов, например `sendmail.cf`.670671Шаблон становится очень сложным, что затрудняет его чтение и понимание. Хуже того, если задача изменится и нужно будет исключить как `bat`, так и `exe` в качестве расширений, шаблон станет ещё более запутанным.672673Отрицательная опережающая проверка разрешает всю эту путаницу:674675`.*[.](?!bat$).*$` Отрицательная опережающая проверка означает: если выражение `bat` не совпадает в этой точке, продолжать проверку по остальной части шаблона; если `bat$` совпадает, весь шаблон не сработает. Завершающая `$` необходима, чтобы гарантировать, что такие имена, как `sample.batch`, где расширение только начинается с `bat`, будут допущены.676677Исключить другое расширение теперь просто: достаточно добавить его как альтернативу внутри проверки. Следующий шаблон исключает имена файлов, оканчивающиеся на `bat` или `exe`:678679`.*[.](?!bat$|exe$).*$`680681## Изменение строк682683До этого момента мы просто выполняли поиск в статической строке. Регулярные выражения также часто используются для изменения строк различными способами с помощью следующих методов шаблона:684685| Метод/Атрибут | Назначение |686| --- | --- |687| `split()` | Разделяет строку на список, разбивая её везде, где совпадает RE |688| `sub()` | Находит все подстроки, совпадающие с RE, и заменяет их на другую строку |689| `subn()` | Делает то же самое, что и `sub()`, но возвращает новую строку и количество замен |690691### Разделение строк692693Метод `split()` шаблона разделяет строку везде, где встречается регулярное выражение, и возвращает список фрагментов. Он похож на метод `split()` строк, но предоставляет гораздо больше возможностей для выбора разделителей; строковый метод `split()` поддерживает только разбиение по пробельным символам или по фиксированной строке. Как и следовало ожидать, существует также функция уровня модуля [`re.split()`](https://python-all.ru/3.4/library/re.html#re.split).694695#### `.split(string[, maxsplit=0])`696697Разделяет *строку* по совпадениям регулярного выражения. Если в RE используются захватывающие круглые скобки, то их содержимое также будет возвращено как часть результирующего списка. Если *maxsplit* не равно нулю, выполняется не более *maxsplit* разбиений.698699Количество выполняемых разбиений можно ограничить, передав значение для *maxsplit*. Если *maxsplit* не равно нулю, будет выполнено не более *maxsplit* разбиений, а остаток строки возвращается как последний элемент списка. В следующем примере разделителем является любая последовательность неалфавитно-цифровых символов.700701```python702>>> p = re.compile(r'\W+')703>>> p.split('This is a test, short and sweet, of split().')704['This', 'is', 'a', 'test', 'short', 'and', 'sweet', 'of', 'split', '']705>>> p.split('This is a test, short and sweet, of split().', 3)706['This', 'is', 'a', 'test, short and sweet, of split().']707```708709Иногда нужно знать не только текст между разделителями, но и сам разделитель. Если в RE используются захватывающие круглые скобки, то их значения также возвращаются как часть списка. Сравните следующие вызовы:710711```python712>>> p = re.compile(r'\W+')713>>> p2 = re.compile(r'(\W+)')714>>> p.split('This... is a test.')715['This', 'is', 'a', 'test', '']716>>> p2.split('This... is a test.')717['This', '... ', 'is', ' ', 'a', ' ', 'test', '.', '']718```719720Функция уровня модуля [`re.split()`](https://python-all.ru/3.4/library/re.html#re.split) добавляет регулярное выражение в качестве первого аргумента, но в остальном делает то же самое.721722```python723>>> re.split('[\W]+', 'Words, words, words.')724['Words', 'words', 'words', '']725>>> re.split('([\W]+)', 'Words, words, words.')726['Words', ', ', 'words', ', ', 'words', '.', '']727>>> re.split('[\W]+', 'Words, words, words.', 1)728['Words', 'words, words.']729```730731### Поиск и замена732733Еще одна распространенная задача – найти все совпадения с шаблоном и заменить их другой строкой. Метод `sub()` принимает значение замены, которое может быть строкой или функцией, и обрабатываемую строку.734735#### `.sub(replacement, string[, count=0])`736737Возвращает строку, полученную заменой самых левых непересекающихся совпадений RE в *строке* на замену *replacement*. Если шаблон не найден, *строка* возвращается без изменений.738739Необязательный аргумент *count* – максимальное количество заменяемых вхождений шаблона; *count* должно быть неотрицательным целым числом. Значение по умолчанию 0 означает замену всех вхождений.740741Вот простой пример использования метода `sub()`. Он заменяет названия цветов на слово `colour`:742743```python744>>> p = re.compile( '(blue|white|red)')745>>> p.sub( 'colour', 'blue socks and red shoes')746'colour socks and colour shoes'747>>> p.sub( 'colour', 'blue socks and red shoes', count=1)748'colour socks and red shoes'749```750751Метод `subn()` делает то же самое, но возвращает кортеж из двух элементов, содержащий новую строку и количество выполненных замен:752753```python754>>> p = re.compile( '(blue|white|red)')755>>> p.subn( 'colour', 'blue socks and red shoes')756('colour socks and colour shoes', 2)757>>> p.subn( 'colour', 'no colours at all')758('no colours at all', 0)759```760761Пустые совпадения заменяются только тогда, когда они не примыкают к предыдущему совпадению.762763```python764>>> p = re.compile('x*')765>>> p.sub('-', 'abxd')766'-a-b-d-'767```768769Если *replacement* – строка, все управляющие последовательности с обратной косой чертой в ней обрабатываются. То есть `\n` преобразуется в символ новой строки, `\r` – в символ возврата каретки и так далее. Неизвестные управляющие последовательности, такие как `\j`, остаются без изменений. Обратные ссылки, например `\6`, заменяются подстрокой, соответствующей группе в регулярном выражении. Это позволяет включать фрагменты исходного текста в результирующую строку замены.770771Этот пример находит слово `section`, за которым следует строка, заключенная в `{`, `}`, и заменяет `section` на `subsection`:772773```python774>>> p = re.compile('section{ ( [^}]* ) }', re.VERBOSE)775>>> p.sub(r'subsection{\1}','section{First} section{second}')776'subsection{First} subsection{second}'777```778779Существует также синтаксис для ссылки на именованные группы, определяемый синтаксисом `(?P<name>...)`. `\g<name>` использует подстроку, совпавшую с группой с именем `name`, а `\g<number>` – соответствующий номер группы. Таким образом, `\g<2>` эквивалентно `\2`, но не вызывает неоднозначности в строке замены, такой как `\g<2>0`. (`\20` было бы интерпретировано как ссылка на группу 20, а не как ссылка на группу 2, за которой следует литеральный символ `'0'`.) Следующие подстановки эквивалентны, но используют все три варианта строки замены.780781```python782>>> p = re.compile('section{ (?P<name> [^}]* ) }', re.VERBOSE)783>>> p.sub(r'subsection{\1}','section{First}')784'subsection{First}'785>>> p.sub(r'subsection{\g<1>}','section{First}')786'subsection{First}'787>>> p.sub(r'subsection{\g<name>}','section{First}')788'subsection{First}'789```790791*replacement* также может быть функцией, что даёт ещё больше контроля. Если *replacement* является функцией, она вызывается для каждого непересекающегося вхождения *pattern*. При каждом вызове функции передаётся аргумент [*объект совпадения*](https://python-all.ru/3.4/library/re.html#match-objects) и она может использовать эту информацию для вычисления нужной строки замены и возврата её.792793В следующем примере функция замены преобразует десятичные числа в шестнадцатеричные:794795```python796>>> def hexrepl(match):797... "Return the hex string for a decimal number"798... value = int(match.group())799... return hex(value)800...801>>> p = re.compile(r'\d+')802>>> p.sub(hexrepl, 'Call 65490 for printing, 49152 for user code.')803'Call 0xffd2 for printing, 0xc000 for user code.'804```805806При использовании функции уровня модуля [`re.sub()`](https://python-all.ru/3.4/library/re.html#re.sub) шаблон передается первым аргументом. Шаблон может быть предоставлен как объект или как строка; если нужно указать флаги регулярного выражения, необходимо либо использовать объект шаблона в качестве первого параметра, либо использовать встроенные модификаторы в строке шаблона, например, `sub("(?i)b+", "x", "bbbb BBBB")` возвращает `'x x'`.807808## Распространенные проблемы809810Регулярные выражения – мощный инструмент для некоторых приложений, но в некоторых аспектах их поведение не интуитивно и иногда они ведут себя не так, как можно было бы ожидать. В этом разделе будут указаны некоторые наиболее распространённые ошибки.811812### Применение строковых методов813814Иногда использование модуля [`re`](https://python-all.ru/3.4/library/re.html#module-re) является ошибкой. Если вы сопоставляете фиксированную строку или одиночный класс символов и не используете никаких возможностей [`re`](https://python-all.ru/3.4/library/re.html#module-re), таких как флаг `IGNORECASE`, то полная мощь регулярных выражений может не понадобиться. У строк есть несколько методов для выполнения операций с фиксированными строками, и они обычно гораздо быстрее, поскольку реализация представляет собой один небольшой цикл на C, оптимизированный для этой цели, вместо большого, более универсального движка регулярных выражений.815816Один из примеров – замена одной фиксированной строки на другую; например, можно заменить `word` на `deed`. `re.sub()` кажется подходящей функцией для этого, но рассмотрите метод `replace()`. Обратите внимание, что `replace()` также заменит `word` внутри слов, превратив `swordfish` в `sdeedfish`, но наивное RE `word` сделало бы то же самое. (Чтобы избежать замены в частях слов, шаблон должен быть `\bword\b`, чтобы требовалось наличие границы слова с обеих сторон от `word`. Это выходит за рамки возможностей `replace()`.)817818Еще одна распространенная задача – удаление всех вхождений одного символа из строки или замена его другим символом. Это можно сделать с помощью чего-то вроде `re.sub('\n', ' ', S)`, но `translate()` способен выполнить обе задачи и будет быстрее любой операции с регулярными выражениями.819820Короче говоря, прежде чем обращаться к модулю [`re`](https://python-all.ru/3.4/library/re.html#module-re), подумайте, можно ли решить вашу задачу с помощью более быстрого и простого строкового метода.821822### match() и search()823824Функция `match()` проверяет только, совпадает ли регулярное выражение в начале строки, тогда как `search()` будет сканировать строку вперед в поисках совпадения. Важно помнить об этом различии. Помните, `match()` сообщит об успешном совпадении только в том случае, если оно начинается с позиции 0; если совпадение не начинается с нуля, `match()` *не* сообщит о нем.825826```python827>>> print(re.match('super', 'superstition').span())828(0, 5)829>>> print(re.match('super', 'insuperable'))830None831```832833С другой стороны, `search()` будет сканировать строку вперед, сообщая о первом найденном совпадении.834835```python836>>> print(re.search('super', 'superstition').span())837(0, 5)838>>> print(re.search('super', 'insuperable').span())839(2, 7)840```841842Иногда возникает соблазн продолжать использовать [`re.match()`](https://python-all.ru/3.4/library/re.html#re.match) и просто добавить `.*` в начало регулярного выражения. Сопротивляйтесь этому искушению и используйте [`re.search()`](https://python-all.ru/3.4/library/re.html#re.search). Компилятор регулярных выражений выполняет некоторый анализ, чтобы ускорить поиск совпадения. Один из таких анализов определяет, каким должен быть первый символ совпадения; например, шаблон, начинающийся с `Crow`, должен начинаться с `'C'`. Этот анализ позволяет движку быстро просматривать строку в поисках начального символа, пробуя полное совпадение только в том случае, если найден `'C'`.843844Добавление `.*` сводит на нет эту оптимизацию, требуя сканирования до конца строки и последующего возврата для поиска совпадения с остальной частью регулярного выражения. Вместо этого используйте [`re.search()`](https://python-all.ru/3.4/library/re.html#re.search).845846### Жадный и нежадный режимы847848При повторении регулярного выражения, как в `a*`, результирующее действие – поглотить как можно больше шаблона. Этот факт часто подводит при попытке сопоставить пару сбалансированных разделителей, например угловые скобки, окружающие HTML-тег. Наивный шаблон для сопоставления одного HTML-тега не работает из-за жадной природы `.*`.849850```python851>>> s = '<html><head><title>Title</title>'852>>> len(s)85332854>>> print(re.match('<.*>', s).span())855(0, 32)856>>> print(re.match('<.*>', s).group())857<html><head><title>Title</title>858```859860Регулярное выражение находит `'<'` в `<html>`, и `.*` поглощает остальную часть строки. Однако в регулярном выражении еще осталась часть, и `>` не может совпасть в конце строки, поэтому движку регулярных выражений приходится возвращаться посимвольно, пока он не найдет совпадение для `>`. Итоговое совпадение простирается от `'<'` в `<html>` до `'>'` в `</title>`, что не является желаемым результатом.861862В этом случае решение – использовать нежадные квантификаторы `*?`, `+?`, `??` или `{m,n}?`, которые сопоставляют как можно *меньше* текста. В приведенном выше примере `'>'` пробуется сразу после того, как первое `'<'` совпадает, и когда это не удается, движок продвигается на один символ за раз, повторяя попытку `'>'` на каждом шаге. Это дает правильный результат:863864```python865>>> print(re.match('<.*?>', s).group())866<html>867```868869(Заметьте, что разбор HTML или XML с помощью регулярных выражений – дело мучительное. Быстрые и грязные шаблоны справятся с обычными случаями, но у HTML и XML есть особые случаи, которые сломают очевидное регулярное выражение; к тому времени, как вы напишете регулярное выражение, обрабатывающее все возможные случаи, шаблоны станут *очень* сложными. Используйте для таких задач модуль разбора HTML или XML.)870871### Использование re.VERBOSE872873К этому моменту вы, вероятно, заметили, что регулярные выражения – это очень компактная запись, но они не слишком читаемы. RE умеренной сложности могут превратиться в длинные наборы обратных слешей, скобок и метасимволов, что затрудняет их чтение и понимание.874875Для таких регулярных выражений может быть полезно указать флаг `re.VERBOSE` при компиляции, так как он позволяет форматировать регулярное выражение более наглядно.876877Флаг `re.VERBOSE` имеет несколько эффектов. Пробельные символы в регулярном выражении, которые *не находятся* внутри класса символов, игнорируются. Это означает, что выражение, такое как `dog | cat`, эквивалентно менее читаемому `dog|cat`, но `[a b]` все равно будет соответствовать символам `'a'`, `'b'` или пробелу. Кроме того, можно размещать комментарии внутри регулярного выражения; комментарии начинаются с символа `#` и продолжаются до следующего перевода строки. При использовании с тройными кавычками это позволяет форматировать регулярные выражения более аккуратно:878879```python880pat = re.compile(r"""881 \s* # Skip leading whitespace882 (?P<header>[^:]+) # Header name883 \s* : # Whitespace, and a colon884 (?P<value>.*?) # The header's value -- *? used to885 # lose the following trailing whitespace886 \s*$ # Trailing whitespace to end-of-line887""", re.VERBOSE)888```889890Это гораздо читаемее, чем:891892```python893pat = re.compile(r"\s*(?P<header>[^:]+)\s*:(?P<value>.*?)\s*$")894```895896## Обратная связь897898Регулярные выражения – сложная тема. Помог ли вам этот документ понять их? Были ли части, которые остались неясными, или проблемы, с которыми вы столкнулись и которые не были здесь рассмотрены? Если да, отправьте предложения по улучшению автору.899900Самая полная книга по регулярным выражениям – это, почти наверняка, «Mastering Regular Expressions» Джеффри Фридла, издательство O'Reilly. К сожалению, она полностью сосредоточена на реализациях регулярных выражений в Perl и Java и не содержит никакого материала по Python, поэтому она не пригодится в качестве справочника для программирования на Python. (Первое издание охватывало ныне удаленный модуль Python `regex`, что вам не очень поможет.) Подумайте о том, чтобы взять ее в библиотеке.901