regex.md
1> **Источник:** https://python-all.ru/2.7/howto/regex.html2>3> «Документация Python на русском» – неофициальный перевод официальной документации Python: версии от 2.6 до 3.16, полнотекстовый поиск, английский оригинал рядом с переводом. Эта Markdown-версия страницы предназначена для работы с LLM: вставьте её в ChatGPT, Claude или Cursor.45---67# Регулярные выражения: руководство89**Автор**1011A.M. Kuchling \<[amk@amk.ca](https://python-all.ru/2.7/howto/regex.html)\>1213Аннотация1415Этот документ – вводное руководство по использованию регулярных выражений в Python с модулем [`re`](https://python-all.ru/2.7/library/re.html#module-re). Оно даёт более плавное введение, чем соответствующий раздел в справочнике по библиотеке.1617## Введение1819Модуль [`re`](https://python-all.ru/2.7/library/re.html#module-re) был добавлен в Python 1.5 и предоставляет шаблоны регулярных выражений в стиле Perl. Более ранние версии Python поставлялись с модулем `regex`, который предоставлял шаблоны в стиле Emacs. Модуль `regex` был полностью удалён в Python 2.5.2021Регулярные выражения (называемые RE, или regex, или regex-шаблоны) – это, по сути, крошечный, узкоспециализированный язык программирования, встроенный в Python и доступный через модуль [`re`](https://python-all.ru/2.7/library/re.html#module-re). С помощью этого небольшого языка вы задаёте правила для множества возможных строк, которые хотите сопоставить; это множество может содержать английские предложения, адреса электронной почты, команды TeX или что угодно. Затем можно задавать вопросы вроде «Соответствует ли эта строка шаблону?» или «Есть ли совпадение с шаблоном где-либо в этой строке?». С помощью RE можно также изменять строку или разделять её различными способами.2223Шаблоны регулярных выражений компилируются в набор байткодов, которые затем выполняются механизмом сопоставления, написанным на C. Для продвинутого использования может потребоваться тщательно следить за тем, как механизм будет выполнять данное RE, и писать RE определённым образом, чтобы получить байткод, работающий быстрее. Оптимизация не рассматривается в этом документе, поскольку она требует хорошего понимания внутреннего устройства механизма сопоставления.2425Язык регулярных выражений относительно мал и ограничен, поэтому не все задачи обработки строк можно решить с помощью регулярных выражений. Существуют также задачи, которые *можно* выполнить с помощью регулярных выражений, но выражения оказываются очень сложными. В таких случаях, возможно, лучше написать код на Python для обработки; хотя код на Python будет медленнее, чем сложное регулярное выражение, он, вероятно, будет более понятным.2627## Простые шаблоны2829Начнём с изучения самых простых регулярных выражений. Поскольку регулярные выражения используются для работы со строками, начнём с самой распространённой задачи: сопоставления символов.3031Подробное объяснение теоретических основ регулярных выражений (детерминированные и недетерминированные конечные автоматы) можно найти почти в любом учебнике по написанию компиляторов.3233### Сопоставление символов3435Большинство букв и символов сопоставляются сами с собой. Например, регулярное выражение `test` будет точно соответствовать строке `test`. (Можно включить регистронезависимый режим, который позволит этому РВ сопоставляться также с `Test` или `TEST`; подробнее об этом позже.)3637Из этого правила есть исключения: некоторые символы являются специальными *метасимволами* и не сопоставляются сами с собой. Вместо этого они указывают, что должно быть сопоставлено что-то необычное, или влияют на другие части РВ, повторяя их или меняя их значение. Значительная часть этого документа посвящена обсуждению различных метасимволов и их действий.3839Вот полный список метасимволов; их значения будут рассмотрены в остальной части этого HOWTO.4041```text42. ^ $ * + ? { } [ ] \ | ( )43```4445Первые метасимволы, которые мы рассмотрим, – это `[` и `]`. Они используются для определения символьного класса – набора символов, которые нужно сопоставить. Символы можно перечислить по отдельности, или указать диапазон символов, задав два символа и разделив их дефисом `'-'`. Например, `[abc]` будет соответствовать любому из символов `a`, `b` или `c`; это то же самое, что `[a-c]`, где диапазон выражает тот же набор символов. Если нужно сопоставлять только строчные буквы, РВ будет `[a-z]`.4647Метасимволы не активны внутри классов. Например, `[akm$]` будет соответствовать любому из символов `'a'`, `'k'`, `'m'` или `'$'`; `'$'` обычно является метасимволом, но внутри символьного класса он лишается своего особого значения.4849Можно сопоставлять символы, не входящие в класс, *дополняя* набор. Это обозначается включением `'^'` в качестве первого символа класса. Например, `[^5]` будет соответствовать любому символу, кроме `'5'`. Если символ крышки (^) появляется в другом месте символьного класса, он не имеет специального значения. Например: `[5^]` будет соответствовать либо `'5'`, либо `'^'`.5051Пожалуй, самый важный метасимвол – обратная косая черта, `\`. Как и в строковых литералах Python, за обратной косой чертой могут следовать различные символы для обозначения специальных последовательностей. Она также используется для экранирования всех метасимволов, чтобы их можно было сопоставлять в шаблонах; например, если нужно сопоставить `[` или `\`, можно поставить перед ними обратную косую черту, чтобы убрать их специальное значение: `\[` или `\\`.5253Некоторые специальные последовательности, начинающиеся с `'\'`, представляют собой предопределённые наборы символов, которые часто бывают полезны, например набор цифр, набор букв или набор любых символов, не являющихся пробельными. Следующие предопределённые специальные последовательности – это подмножество доступных. Эквивалентные классы предназначены для шаблонов байтовых строк. Полный список последовательностей и расширенных определений классов для шаблонов Unicode-строк см. в последней части [Синтаксис регулярных выражений](https://python-all.ru/2.7/library/re.html#re-syntax).5455**`\d`**5657Соответствует любой десятичной цифре; эквивалентно классу `[0-9]`.5859**`\D`**6061Соответствует любому нецифровому символу; эквивалентно классу `[^0-9]`.6263**`\s`**6465Соответствует любому пробельному символу; эквивалентно классу `[ \t\n\r\f\v]`.6667**`\S`**6869Соответствует любому непробельному символу; эквивалентно классу `[^ \t\n\r\f\v]`.7071**`\w`**7273Соответствует любому буквенно-цифровому символу; эквивалентно классу `[a-zA-Z0-9_]`.7475**`\W`**7677Соответствует любому не буквенно-цифровому символу; эквивалентно классу `[^a-zA-Z0-9_]`.7879Эти последовательности можно включать внутрь символьного класса. Например, `[\s,.]` – это символьный класс, который будет соответствовать любому пробельному символу, или `','`, или `'.'`.8081Последний метасимвол в этом разделе – `.`. Он соответствует любому символу, кроме символа новой строки, и есть альтернативный режим (`re.DOTALL`), в котором он будет соответствовать даже новой строке. `'.'` часто используется, когда нужно сопоставить «любой символ».8283### Повторяющиеся элементы8485Возможность сопоставлять различные наборы символов – это первое, что регулярные выражения умеют делать, чего уже нельзя сделать с помощью методов строк. Однако если бы это была единственная дополнительная возможность регулярных выражений, они не были бы большим достижением. Ещё одна возможность – указывать, что части РВ должны повторяться определённое количество раз.8687Первый метасимвол для повторения, который мы рассмотрим, – это `*`. `*` не соответствует литеральному символу `*`; вместо этого он указывает, что предыдущий символ может быть сопоставлен ноль или более раз, а не ровно один раз.8889Например, `ca*t` совпадает с `ct` (0 `a` символов), `cat` (1 `a`), `caaat` (3 `a` символов) и так далее. Движок регулярных выражений имеет различные внутренние ограничения, обусловленные размером типа `int` в C, которые не позволят ему сопоставить более 2 миллиардов `a` символов; скорее всего, у вас недостаточно памяти для создания такой длинной строки, так что вы не должны столкнуться с этим ограничением.9091Повторения, такие как `*`, являются *жадными*; при повторении РВ движок сопоставления будет стараться повторить его как можно больше раз. Если более поздние части шаблона не совпадают, движок сопоставления отступает и пробует снова с меньшим числом повторений.9293Пошаговый пример прояснит это. Рассмотрим выражение `a[bcd]*b`. Оно соответствует букве `'a'`, нулю или более букв из класса `[bcd]` и заканчивается символом `'b'`. Теперь представим, что это РВ сопоставляется со строкой `abcbd`.9495| Шаг | Совпадение | Пояснение |96| --- | --- | --- |97| 1 | `a` | `a` в РВ совпадает. |98| 2 | `abcbd` | Движок сопоставляет `[bcd]*`, продвигаясь так далеко, как может, то есть до конца строки. |99| 3 | *Неудача* | Движок пытается сопоставить `b`, но текущая позиция находится в конце строки, поэтому попытка не удаётся. |100| 4 | `abcb` | Отступ, чтобы `[bcd]*` сопоставляло на один символ меньше. |101| 5 | *Неудача* | Снова попробовать `b`, но текущая позиция находится на последнем символе, которым является `'d'`. |102| 6 | `abc` | Снова отступ, чтобы `[bcd]*` сопоставляло только `bc`. |103| 6 | `abcb` | Снова попробовать `b`. На этот раз символ в текущей позиции – `'b'`, поэтому совпадение находится. |104105Конец регулярного выражения достигнут, и оно сопоставило `abcb`. Это показывает, как движок сопоставления сначала заходит так далеко, как может, а если совпадение не найдено, постепенно отступает и снова и снова перебирает остальную часть регулярного выражения. Он будет отступать до тех пор, пока не попробует ноль совпадений для `[bcd]*`, и если это впоследствии не удастся, движок приходит к выводу, что строка вообще не соответствует регулярному выражению.106107Ещё один повторяющийся метасимвол – `+`, который соответствует одному или более вхождений. Обратите внимание на разницу между `*` и `+`; `*` соответствует *нулю* или более раз, так что повторяющийся элемент может вообще отсутствовать, тогда как `+` требует как минимум *одного* повторения. Для похожего примера, `ca+t` будет соответствовать `cat` (1 `a`), `caaat` (3 `a`), но не будет соответствовать `ct`.108109Есть ещё два повторяющихся квантификатора. Символ вопросительного знака `?`, соответствует одному или нулю повторений; его можно считать пометкой чего-либо как необязательного. Например, `home-?brew` соответствует либо `homebrew`, либо `home-brew`.110111Самый сложный повторяющийся квантификатор – `{m,n}`, где *m* и *n* – десятичные целые числа. Этот квантификатор означает, что должно быть как минимум *m* повторений и как максимум *n*. Например, `a/{1,3}b` соответствует `a/b`, `a//b` и `a///b`. Он не соответствует `ab` (без слэшей) или `a////b` (с четырьмя).112113Можно опустить либо *m*, либо *n*; в этом случае подразумевается разумное значение для отсутствующего параметра. Пропуск *m* интерпретируется как нижняя граница 0, а пропуск *n* приводит к верхней границе бесконечности – на самом деле верхняя граница – это упомянутый ранее предел в 2 миллиарда, но её можно считать бесконечностью.114115Можно заметить, что три остальных квантификатора можно выразить через эту нотацию. `{0,}` – то же, что `*`, `{1,}` эквивалентен `+`, а `{0,1}` – то же, что `?`. Лучше использовать `*`, `+` или `?`, когда это возможно, просто потому что они короче и легче читаются.116117## Использование регулярных выражений118119Теперь, когда мы рассмотрели некоторые простые регулярные выражения, как же их использовать в Python? Модуль [`re`](https://python-all.ru/2.7/library/re.html#module-re) предоставляет интерфейс к движку регулярных выражений, позволяя компилировать регулярные выражения в объекты и затем выполнять с ними сопоставление.120121### Компиляция регулярных выражений122123Регулярные выражения компилируются в объекты шаблонов, которые имеют методы для различных операций, таких как поиск совпадений с шаблоном или выполнение подстановок в строке.124125```python126>>> import re127>>> p = re.compile('ab*')128>>> p 129<_sre.SRE_Pattern object at 0x...>130```131132[`re.compile()`](https://python-all.ru/2.7/library/re.html#re.compile) также принимает необязательный аргумент *flags*, используемый для включения различных специальных возможностей и вариантов синтаксиса. Мы рассмотрим доступные настройки позже, а пока достаточно одного примера:133134```python135>>> p = re.compile('ab*', re.IGNORECASE)136```137138RE передаётся в [`re.compile()`](https://python-all.ru/2.7/library/re.html#re.compile) в виде строки. RE обрабатываются как строки, поскольку регулярные выражения не являются частью ядра языка Python, и для их записи не был создан специальный синтаксис. (Существуют приложения, которым RE вообще не нужны, поэтому нет необходимости раздувать спецификацию языка, включая их.) Вместо этого модуль [`re`](https://python-all.ru/2.7/library/re.html#module-re) – это просто модуль-расширение на C, поставляемый с Python, точно так же, как модули [`socket`](https://python-all.ru/2.7/library/socket.html#module-socket) или [`zlib`](https://python-all.ru/2.7/library/zlib.html#module-zlib).139140Размещение RE в строках сохраняет язык Python более простым, но имеет один недостаток, который является темой следующего раздела.141142### Проблема обратной косой черты143144Как было сказано ранее, регулярные выражения используют символ обратной косой черты (`'\'`) для обозначения специальных форм или для использования специальных символов без их специального значения. Это конфликтует с использованием того же символа в строковых литералах Python для той же цели.145146Допустим, нужно написать RE, которое бы совпадало со строкой `\section`, которая может встретиться в файле LaTeX. Чтобы понять, что написать в коде программы, начнём с желаемой строки для совпадения. Затем необходимо экранировать все обратные слеши и другие метасимволы, поставив перед ними обратную косую черту, в результате получится строка `\\section`. Итоговая строка, которую нужно передать в [`re.compile()`](https://python-all.ru/2.7/library/re.html#re.compile), должна быть `\\section`. Однако, чтобы представить это в виде строкового литерала Python, оба обратных слеша нужно экранировать *снова*.147148| Символы | Этап |149| --- | --- |150| `\section` | Строка для совпадения |151| `\\section` | Экранированный обратный слеш для [`re.compile()`](https://python-all.ru/2.7/library/re.html#re.compile) |152| `"\\\\section"` | Экранированные обратные слеши для строкового литерала |153154Короче говоря, для сопоставления с литералом обратной косой черты нужно записать `'\\\\'` в качестве строки RE, потому что регулярное выражение должно быть `\\`, а каждый обратный слеш должен быть представлен как `\\` внутри обычного строкового литерала Python. В RE, где обратные слеши встречаются многократно, это приводит к множеству повторных слешей и делает итоговые строки трудными для понимания.155156Решение – использовать сырые строки Python для регулярных выражений; обратные слеши не обрабатываются особым образом в строковом литерале с префиксом `'r'`, поэтому `r"\n"` – это двухсимвольная строка, содержащая `'\'` и `'n'`, а `"\n"` – односимвольная строка, содержащая перевод строки. Регулярные выражения часто записываются в коде Python с использованием этой сырой строковой записи.157158| Обычная строка | Сырая строка |159| --- | --- |160| `"ab*"` | `r"ab*"` |161| `"\\\\section"` | `r"\\section"` |162| `"\\w+\\s+\\1"` | `r"\w+\s+\1"` |163164### Выполнение поиска совпадений165166Как только у вас есть объект, представляющий скомпилированное регулярное выражение, что с ним делать? Объекты Pattern имеют несколько методов и атрибутов. Здесь будут рассмотрены только самые важные; за полным списком обращайтесь к документации [`re`](https://python-all.ru/2.7/library/re.html#module-re).167168| Метод/Атрибут | Назначение |169| --- | --- |170| `match()` | Определяет, совпадает ли RE в начале строки. |171| `search()` | Сканирует строку в поисках любого места, где данное регулярное выражение совпадает. |172| `findall()` | Находит все подстроки, совпадающие с регулярным выражением, и возвращает их в виде списка. |173| `finditer()` | Find all substrings where the RE matches, and returns them as an [iterator](https://python-all.ru/2.7/glossary.html#term-iterator). |174175`match()` и `search()` возвращают `None`, если совпадение не найдено. В случае успеха возвращается экземпляр [объекта совпадения](https://python-all.ru/2.7/library/re.html#match-objects), содержащий информацию о совпадении: где оно начинается и заканчивается, совпавшую подстроку и другое.176177Вы можете изучить это, интерактивно экспериментируя с модулем [`re`](https://python-all.ru/2.7/library/re.html#module-re). Если у вас установлен Tkinter, вы также можете посмотреть на [Tools/scripts/redemo.py](https://python-all.ru/src/2.7/Tools/scripts/redemo.py) – демонстрационную программу, входящую в дистрибутив Python. Она позволяет вводить регулярные выражения и строки и показывает, совпадает ли выражение или нет. `redemo.py` может быть весьма полезен при отладке сложного регулярного выражения. [Kodos](https://python-all.ru/2.7/howto/regex.html) Фила Шварца – ещё один интерактивный инструмент для разработки и тестирования шаблонов регулярных выражений.178179В данном HOWTO используется стандартный интерпретатор Python для примеров. Сначала запустите интерпретатор Python, импортируйте модуль [`re`](https://python-all.ru/2.7/library/re.html#module-re) и скомпилируйте регулярное выражение:180181```python182Python 2.2.2 (#1, Feb 10 2003, 12:57:01)183>>> import re184>>> p = re.compile('[a-z]+')185>>> p #doctest: +ELLIPSIS186<_sre.SRE_Pattern object at 0x...>187```188189Теперь можно попробовать сопоставить различные строки с РВ `[a-z]+`. Пустая строка не должна совпадать, поскольку `+` означает «одно или более повторений». `match()` в этом случае должен вернуть `None`, что заставит интерпретатор ничего не выводить. Можно явно напечатать результат `match()`, чтобы прояснить это.190191```python192>>> p.match("")193>>> print p.match("")194None195```196197Теперь попробуем на строке, которая должна совпадать, например `tempo`. В этом случае `match()` вернёт [объект совпадения](https://python-all.ru/2.7/library/re.html#match-objects), так что сохраните результат в переменную для последующего использования.198199```python200>>> m = p.match('tempo')201>>> m 202<_sre.SRE_Match object at 0x...>203```204205Теперь можно запросить у [объекта match](https://python-all.ru/2.7/library/re.html#match-objects) информацию о соответствующей строке. [Объекты match](https://python-all.ru/2.7/library/re.html#match-objects) также имеют несколько методов и атрибутов; наиболее важные из них:206207| Метод/Атрибут | Назначение |208| --- | --- |209| `group()` | Возвращает строку, совпавшую с РВ |210| `start()` | Возвращает начальную позицию совпадения |211| `end()` | Возвращает конечную позицию совпадения |212| `span()` | Возвращает кортеж с (начало, конец) позициями совпадения |213214Применение этих методов быстро прояснит их смысл:215216```python217>>> m.group()218'tempo'219>>> m.start(), m.end()220(0, 5)221>>> m.span()222(0, 5)223```224225`group()` возвращает подстроку, совпавшую с RE. `start()` и `end()` возвращают начальный и конечный индекс совпадения. `span()` возвращает оба индекса в одном кортеже. Поскольку метод `match()` проверяет совпадение только в начале строки, `start()` всегда будет равен нулю. Однако метод `search()` шаблонов просматривает строку, поэтому в таком случае совпадение может начинаться не с нуля.226227```python228>>> print p.match('::: message')229None230>>> m = p.search('::: message'); print m 231<_sre.SRE_Match object at 0x...>232>>> m.group()233'message'234>>> m.span()235(4, 11)236```237238В реальных программах наиболее распространённый подход – сохранить [объект совпадения](https://python-all.ru/2.7/library/re.html#match-objects) в переменную, а затем проверить, был ли он `None`. Обычно это выглядит так:239240```python241p = re.compile( ... )242m = p.match( 'string goes here' )243if m:244 print 'Match found: ', m.group()245else:246 print 'No match'247```248249Два метода шаблонов возвращают все совпадения для шаблона. `findall()` возвращает список совпавших строк:250251```python252>>> p = re.compile('\d+')253>>> p.findall('12 drummers drumming, 11 pipers piping, 10 lords a-leaping')254['12', '11', '10']255```256257`findall()` должен создать весь список, прежде чем его можно будет вернуть в качестве результата. Метод `finditer()` возвращает последовательность экземпляров [объекта сопоставления](https://python-all.ru/2.7/library/re.html#match-objects) в виде [итератора](https://python-all.ru/2.7/glossary.html#term-iterator). [1](https://python-all.ru/2.7/howto/regex.html#id3)258259```python260>>> iterator = p.finditer('12 drummers drumming, 11 ... 10 ...')261>>> iterator 262<callable-iterator object at 0x...>263>>> for match in iterator:264... print match.span()265...266(0, 2)267(22, 24)268(29, 31)269```270271### Функции уровня модуля272273Вам не нужно создавать объект шаблона и вызывать его методы; модуль [`re`](https://python-all.ru/2.7/library/re.html#module-re) также предоставляет функции верхнего уровня: `match()`, `search()`, `findall()`, `sub()` и так далее. Эти функции принимают те же аргументы, что и соответствующий метод шаблона, с добавлением строки регулярного выражения в качестве первого аргумента, и по-прежнему возвращают либо `None`, либо экземпляр [объекта сопоставления](https://python-all.ru/2.7/library/re.html#match-objects).274275```python276>>> print re.match(r'From\s+', 'Fromage amk')277None278>>> re.match(r'From\s+', 'From amk Thu May 14 19:12:10 1998') 279<_sre.SRE_Match object at 0x...>280```281282Внутри эти функции просто создают объект шаблона и вызывают на нём соответствующий метод. Они также сохраняют скомпилированный объект в кеше, поэтому последующие вызовы с тем же регулярным выражением выполняются быстрее.283284Стоит ли использовать эти функции уровня модуля или лучше получить шаблон и вызывать его методы самостоятельно? Выбор зависит от того, как часто будет использоваться регулярное выражение, и от вашего личного стиля кодирования. Если регулярное выражение используется только в одном месте кода, то, вероятно, функции модуля удобнее. Если программа содержит много регулярных выражений или использует одни и те же в нескольких местах, то, возможно, стоит собрать все определения в одном месте, в разделе кода, который компилирует все регулярные выражения заранее. В качестве примера из стандартной библиотеки приведён фрагмент из устаревшего модуля `xmllib`:285286```python287ref = re.compile( ... )288entityref = re.compile( ... )289charref = re.compile( ... )290starttagopen = re.compile( ... )291```292293Я обычно предпочитаю работать со скомпилированным объектом, даже для одноразового использования, но мало кто будет таким же пуристом в этом вопросе, как я.294295### Флаги компиляции296297Флаги компиляции позволяют изменять некоторые аспекты работы регулярных выражений. Флаги доступны в модуле [`re`](https://python-all.ru/2.7/library/re.html#module-re) под двумя именами: длинным, например `IGNORECASE`, и коротким, однобуквенным, например `I`. (Для знакомых с модификаторами шаблонов Perl: однобуквенные формы используют те же буквы; например, короткая форма [`re.VERBOSE`](https://python-all.ru/2.7/library/re.html#re.VERBOSE) – это [`re.X`](https://python-all.ru/2.7/library/re.html#re.X).) Несколько флагов можно указать с помощью побитового ИЛИ; например, `re.I | re.M` устанавливает оба флага `I` и `M`.298299Ниже приведена таблица доступных флагов, а затем более подробное описание каждого из них.300301| Флаг | Значение |302| --- | --- |303| `DOTALL`, `S` | Заставляет `.` соответствовать любому символу, включая символы новой строки |304| `IGNORECASE`, `I` | Выполняет регистронезависимые сопоставления |305| `LOCALE`, `L` | Выполняет сопоставление с учётом локали |306| `MULTILINE`, `M` | Многострочное сопоставление, затрагивающее `^` и `$` |307| `VERBOSE`, `X` | Включает подробные (verbose) регулярные выражения, которые можно организовать более чисто и понятно. |308| `UNICODE`, `U` | Делает несколько экранирований, таких как `\w`, `\b`, `\s` и `\d`, зависимыми от базы данных символов Unicode. |309310#### `I`311312#### `IGNORECASE`313314Выполняет регистронезависимое сопоставление; классы символов и литеральные строки будут соответствовать буквам, игнорируя регистр. Например, `[A-Z]` будет соответствовать также строчным буквам, а `Spam` будет соответствовать `Spam`, `spam` или `spAM`. Это приведение к нижнему регистру не учитывает текущую локаль; оно будет учитывать её, если также установить флаг `LOCALE`.315316#### `L`317318#### `LOCALE`319320Делает `\w`, `\W`, `\b` и `\B` зависимыми от текущей локали.321322Локали – это возможность библиотеки C, предназначенная для помощи в написании программ, которые учитывают различия языков. Например, при обработке французского текста хотелось бы иметь возможность написать `\w+` для поиска слов, но `\w` соответствует только классу символов `[A-Za-z]`; он не будет соответствовать `'é'` или `'ç'`. Если система настроена правильно и выбрана французская локаль, некоторые C-функции сообщат программе, что `'é'` также следует считать буквой. Установка флага `LOCALE` при компиляции регулярного выражения приведёт к тому, что результирующий скомпилированный объект будет использовать эти C-функции для `\w`; это медленнее, но также позволяет `\w+` находить французские слова, как и ожидается.323324#### `M`325326#### `MULTILINE`327328(`^` и `$` пока не объяснены; они будут представлены в разделе [Дополнительные метасимволы](https://python-all.ru/2.7/howto/regex.html#more-metacharacters).)329330Обычно `^` сопоставляется только в начале строки, а `$` – только в конце строки и непосредственно перед символом новой строки (если он есть) в конце строки. Когда указан этот флаг, `^` сопоставляется в начале строки и в начале каждой строки внутри строки, сразу после каждого символа новой строки. Аналогично, метасимвол `$` сопоставляется в конце строки и в конце каждой строки (непосредственно перед каждым символом новой строки).331332#### `S`333334#### `DOTALL`335336Заставляет специальный символ `'.'` сопоставляться с любым символом, включая символ новой строки; без этого флага `'.'` будет сопоставляться с любым символом, *кроме* символа новой строки.337338#### `U`339340#### `UNICODE`341342Делает `\w`, `\W`, `\b`, `\B`, `\d`, `\D`, `\s` и `\S` зависимыми от базы данных свойств символов Unicode.343344#### `X`345346#### `VERBOSE`347348Этот флаг позволяет писать более читаемые регулярные выражения, предоставляя больше гибкости в их форматировании. Когда этот флаг указан, пробельные символы внутри строки регулярного выражения игнорируются, за исключением случаев, когда пробел находится внутри символьного класса или перед ним стоит неэкранированная обратная косая черта; это позволяет более четко организовывать и расставлять отступы в регулярном выражении. Этот флаг также позволяет помещать комментарии внутри регулярного выражения, которые будут игнорироваться движком; комментарии обозначаются символом `'#'`, который не находится внутри символьного класса и не предваряется неэкранированной обратной косой чертой.349350Например, вот RE, использующий [`re.VERBOSE`](https://python-all.ru/2.7/library/re.html#re.VERBOSE); посмотрите, насколько его легче читать.351352```python353charref = re.compile(r"""354 &[#] # Start of a numeric entity reference355 (356 0[0-7]+ # Octal form357 | [0-9]+ # Decimal form358 | x[0-9a-fA-F]+ # Hexadecimal form359 )360 ; # Trailing semicolon361""", re.VERBOSE)362```363364Без режима verbose регулярное выражение выглядело бы так:365366```python367charref = re.compile("&#(0[0-7]+"368 "|[0-9]+"369 "|x[0-9a-fA-F]+);")370```371372В примере выше автоматическая конкатенация строковых литералов Python использовалась, чтобы разбить RE на более мелкие части, но он всё равно сложнее для понимания, чем версия с [`re.VERBOSE`](https://python-all.ru/2.7/library/re.html#re.VERBOSE).373374## Дополнительные возможности шаблонов375376До сих пор мы рассмотрели только часть возможностей регулярных выражений. В этом разделе мы рассмотрим некоторые новые метасимволы и способы использования групп для извлечения частей сопоставленного текста.377378### Дополнительные метасимволы379380Остались некоторые метасимволы, которые мы ещё не рассмотрели. Большинство из них будет рассмотрено в этом разделе.381382Некоторые из оставшихся метасимволов – это *утверждения нулевой длины*. Они не заставляют движок продвигаться по строке; вместо этого они вообще не потребляют символов и просто возвращают успех или неудачу. Например, `\b` – это утверждение, что текущая позиция находится на границе слова; позиция при этом не меняется `\b` вообще. Это означает, что утверждения нулевой длины не следует повторять, потому что если они совпадают один раз в данной позиции, то они, очевидно, могут совпадать бесконечное число раз.383384**`|`**385386Альтернация, или оператор «или». Если A и B – регулярные выражения, `A|B` будет соответствовать любой строке, которая соответствует либо `A`, либо `B`. `|` имеет очень низкий приоритет, чтобы он работал разумно при альтернации многосимвольных строк. `Crow|Servo` будет соответствовать либо `Crow`, либо `Servo`, а не `Cro`, `'w'` или `'S'`, и `ervo`.387388Чтобы сопоставить литерал `'|'`, используйте `\|` или заключите его в символьный класс, как в `[|]`.389390**`^`**391392Совпадение в начале строк. Если не установлен флаг `MULTILINE`, соответствует только в начале строки. В режиме `MULTILINE` также совпадает сразу после каждого перевода строки внутри строки.393394Например, если требуется найти слово `From` только в начале строки, нужно использовать RE `^From`.395396```python397>>> print re.search('^From', 'From Here to Eternity') 398<_sre.SRE_Match object at 0x...>399>>> print re.search('^From', 'Reciting From Memory')400None401```402403**`$`**404405Совпадение в конце строки, которая определяется как конец строки или любая позиция, за которой следует символ перевода строки.406407```python408>>> print re.search('}$', '{block}') 409<_sre.SRE_Match object at 0x...>410>>> print re.search('}$', '{block} ')411None412>>> print re.search('}$', '{block}\n') 413<_sre.SRE_Match object at 0x...>414```415416Чтобы сопоставить литерал `'$'`, используйте `\$` или заключите его в символьный класс, как в `[$]`.417418**`\A`**419420Совпадение только в начале строки. Вне режима `MULTILINE` `\A` и `^` фактически одинаковы. В режиме `MULTILINE` они различаются: `\A` по-прежнему совпадает только в начале строки, а `^` может совпадать в любом месте строки после символа перевода строки.421422**`\Z`**423424Совпадение только в конце строки.425426**`\b`**427428Граница слова. Это утверждение нулевой длины, которое совпадает только в начале или конце слова. Слово определяется как последовательность буквенно-цифровых символов, поэтому конец слова обозначается пробелом или не-буквенно-цифровым символом.429430Следующий пример находит `class` только когда это целое слово; он не совпадёт, если оно содержится внутри другого слова.431432```python433>>> p = re.compile(r'\bclass\b')434>>> print p.search('no class at all') 435<_sre.SRE_Match object at 0x...>436>>> print p.search('the declassified algorithm')437None438>>> print p.search('one subclass is')439None440```441442Есть две тонкости, которые следует помнить при использовании этой специальной последовательности. Во-первых, это худшее столкновение между строковыми литералами Python и последовательностями регулярных выражений. В строковых литералах Python `\b` – это символ backspace, ASCII-код 8. Если не используются сырые строки, Python преобразует `\b` в backspace, и RE не будет совпадать так, как ожидается. Следующий пример выглядит так же, как предыдущий RE, но в нём опущен `'r'` перед строкой RE.443444```python445>>> p = re.compile('\bclass\b')446>>> print p.search('no class at all')447None448>>> print p.search('\b' + 'class' + '\b') 449<_sre.SRE_Match object at 0x...>450```451452Во-вторых, внутри символьного класса, где это утверждение не используется, `\b` представляет символ backspace для совместимости со строковыми литералами Python.453454**`\B`**455456Ещё одно утверждение нулевой длины, противоположное `\b`, совпадает только когда текущая позиция не находится на границе слова.457458### Группировка459460Часто требуется получить больше информации, чем просто – совпало RE или нет. Регулярные выражения часто используются для разбора строк с помощью RE, разделённого на несколько подгрупп, каждая из которых соответствует интересующему компоненту. Например, строка заголовка RFC-822 разделена на имя заголовка и значение, разделённые `':'`, так:461462```python463From: author@example.com464User-Agent: Thunderbird 1.5.0.9 (X11/20061227)465MIME-Version: 1.0466To: editor@example.com467```468469Это можно обработать, написав регулярное выражение, которое сопоставляет всю строку заголовка, и имеет одну группу для имени заголовка и другую – для значения заголовка.470471Группы обозначаются метасимволами `'('`, `')'`. `'('` и `')'` имеют примерно тот же смысл, что и в математических выражениях: они группируют содержащиеся в них выражения, и содержимое группы можно повторять с помощью повторяющегося квантификатора, например `*`, `+`, `?` или `{m,n}`. Например, `(ab)*` будет соответствовать нулю или более повторениям `ab`.472473```python474>>> p = re.compile('(ab)*')475>>> print p.match('ababababab').span()476(0, 10)477```478479Группы, обозначенные `'('`, `')'`, также захватывают начальный и конечный индекс соответствующего текста; их можно получить, передав аргумент методам `group()`, `start()`, `end()` и `span()`. Группы нумеруются, начиная с 0. Группа 0 присутствует всегда; это всё регулярное выражение, поэтому [методы объекта match](https://python-all.ru/2.7/library/re.html#match-objects) имеют группу 0 в качестве аргумента по умолчанию. Позже мы увидим, как задавать группы, которые не захватывают диапазон соответствующего текста.480481```python482>>> p = re.compile('(a)b')483>>> m = p.match('ab')484>>> m.group()485'ab'486>>> m.group(0)487'ab'488```489490Подгруппы нумеруются слева направо, начиная с 1. Группы могут быть вложенными; чтобы определить номер, нужно просто посчитать открывающие скобки, идя слева направо.491492```python493>>> p = re.compile('(a(b)c)d')494>>> m = p.match('abcd')495>>> m.group(0)496'abcd'497>>> m.group(1)498'abc'499>>> m.group(2)500'b'501```502503`group()` можно передать несколько номеров групп одновременно, и в этом случае она вернёт кортеж, содержащий соответствующие значения для этих групп.504505```python506>>> m.group(2,1,2)507('b', 'abc', 'b')508```509510Метод `groups()` возвращает кортеж, содержащий строки всех подгрупп, от 1 до их количества.511512```python513>>> m.groups()514('abc', 'b')515```516517Обратные ссылки в шаблоне позволяют указать, что содержимое ранее захваченной группы должно также присутствовать в текущей позиции строки. Например, `\1` будет успешным, если точное содержимое группы 1 можно найти в текущей позиции, иначе – нет. Помните, что строковые литералы Python также используют обратную косую черту с числами для включения произвольных символов в строку, поэтому обязательно используйте сырую строку при включении обратных ссылок в RE.518519Например, следующее RE обнаруживает удвоенные слова в строке.520521```python522>>> p = re.compile(r'\b(\w+)\s+\1\b')523>>> p.search('Paris in the the spring').group()524'the the'525```526527Обратные ссылки такого рода редко бывают полезны при простом поиске в строке – существует мало текстовых форматов, которые повторяют данные таким образом, – но вскоре обнаруживается, что они *очень* полезны при выполнении строковых подстановок.528529### Незахватывающие и именованные группы530531Сложные регулярные выражения могут использовать много групп как для захвата интересующих подстрок, так и для группировки и структурирования самого выражения. В сложных RE становится трудно отслеживать номера групп. Существуют две возможности, которые помогают решить эту проблему. Обе используют общий синтаксис расширений регулярных выражений, поэтому сначала рассмотрим его.532533Perl 5 добавил несколько дополнительных возможностей к стандартным регулярным выражениям, и модуль [`re`](https://python-all.ru/2.7/library/re.html#module-re) Python поддерживает большинство из них. Было бы трудно выбрать новые метасимволы из одной клавиши или новые специальные последовательности, начинающиеся с `\`, чтобы представить новые возможности, не делая регулярные выражения Perl запутанно отличными от стандартных. Например, если бы вы выбрали `&` как новый метасимвол, старые выражения считали бы, что `&` – это обычный символ, и не экранировали бы его, записывая `\&` или `[&]`.534535Разработчики Perl выбрали `(?...)` в качестве синтаксиса расширения. `?` сразу после круглой скобки был синтаксической ошибкой, потому что `?` нечего было бы повторять, так что это не вызвало проблем совместимости. Символы сразу после `?` указывают, какое расширение используется, так что `(?=foo)` – это одно (положительная опережающая проверка), а `(?:foo)` – другое (незахватывающая группа, содержащая подвыражение `foo`).536537Python добавляет синтаксис расширения к синтаксису расширения Perl. Если первый символ после вопросительного знака – `P`, это означает, что это расширение, специфичное для Python. В настоящее время существует два таких расширения: `(?P<name>...)` определяет именованную группу, а `(?P=name)` – обратная ссылка на именованную группу. Если будущие версии Perl 5 добавят аналогичные возможности с использованием другого синтаксиса, модуль [`re`](https://python-all.ru/2.7/library/re.html#module-re) будет изменён для поддержки нового синтаксиса, сохраняя при этом синтаксис, специфичный для Python, ради совместимости.538539Теперь, когда мы рассмотрели общий синтаксис расширений, можно вернуться к возможностям, упрощающим работу с группами в сложных регулярных выражениях. Поскольку группы нумеруются слева направо, а сложное выражение может содержать много групп, отслеживать правильную нумерацию становится трудно. Модифицировать такое сложное регулярное выражение тоже утомительно: добавьте новую группу в начале, и номера всех последующих групп изменятся.540541Иногда нужно использовать группу, чтобы собрать часть регулярного выражения, но нет необходимости извлекать содержимое группы. Можно явно указать это, используя незахватывающую группу: `(?:...)`, где `...` можно заменить любым другим регулярным выражением.542543```python544>>> m = re.match("([abc])+", "abc")545>>> m.groups()546('c',)547>>> m = re.match("(?:[abc])+", "abc")548>>> m.groups()549()550```551552За исключением того, что нельзя извлечь содержимое того, что сопоставила группа, незахватывающая группа ведёт себя точно так же, как захватывающая: в неё можно поместить что угодно, повторить её с помощью метасимвола повторения, такого как `*`, и вложить в другие группы (захватывающие или незахватывающие). `(?:...)` особенно полезен при изменении существующего шаблона, поскольку можно добавлять новые группы, не меняя нумерацию всех остальных групп. Стоит упомянуть, что нет разницы в производительности поиска между захватывающими и незахватывающими группами; ни одна из форм не быстрее другой.553554Более значимая возможность – именованные группы: вместо обращения по номерам группы можно ссылаться по имени.555556Синтаксис именованной группы – одно из расширений, специфичных для Python: `(?P<name>...)`. *name* – это, очевидно, имя группы. Именованные группы ведут себя точно так же, как захватывающие группы, и дополнительно связывают имя с группой. Методы [объекта сопоставления](https://python-all.ru/2.7/library/re.html#match-objects), работающие с захватывающими группами, принимают либо целые числа, ссылающиеся на группу по номеру, либо строки, содержащие имя нужной группы. Именованные группы по-прежнему имеют номера, поэтому вы можете получить информацию о группе двумя способами:557558```python559>>> p = re.compile(r'(?P<word>\b\w+\b)')560>>> m = p.search( '(((( Lots of punctuation )))' )561>>> m.group('word')562'Lots'563>>> m.group(1)564'Lots'565```566567Именованные группы удобны, поскольку позволяют использовать легко запоминающиеся имена вместо запоминания номеров. Вот пример RE из модуля [`imaplib`](https://python-all.ru/2.7/library/imaplib.html#module-imaplib):568569```python570InternalDate = re.compile(r'INTERNALDATE "'571 r'(?P<day>[ 123][0-9])-(?P<mon>[A-Z][a-z][a-z])-'572 r'(?P<year>[0-9][0-9][0-9][0-9])'573 r' (?P<hour>[0-9][0-9]):(?P<min>[0-9][0-9]):(?P<sec>[0-9][0-9])'574 r' (?P<zonen>[-+])(?P<zoneh>[0-9][0-9])(?P<zonem>[0-9][0-9])'575 r'"')576```577578Очевидно, что гораздо проще получить `m.group('zonem')`, чем запоминать, что нужно получить группу 9.579580Синтаксис обратных ссылок в выражении, таком как `(...)\1`, относится к номеру группы. Естественно, существует вариант, использующий имя группы вместо номера. Это ещё одно расширение Python: `(?P=name)` указывает, что содержимое группы с именем *name* должно снова совпадать в текущей позиции. Регулярное выражение для поиска удвоенных слов, `\b(\w+)\s+\1\b`, можно также записать как `\b(?P<word>\w+)\s+(?P=word)\b`:581582```python583>>> p = re.compile(r'\b(?P<word>\w+)\s+(?P=word)\b')584>>> p.search('Paris in the the spring').group()585'the the'586```587588### Опережающие проверки589590Ещё одна проверка нулевой ширины – опережающая проверка. Опережающие проверки бывают положительными и отрицательными и выглядят так:591592**`(?=...)`**593594Положительная опережающая проверка. Она успешна, если вложенное регулярное выражение, представленное здесь как `...`, успешно совпадает в текущей позиции, и неуспешна в противном случае. Но как только вложенное выражение проверено, движок сопоставления не продвигается; остальная часть шаблона проверяется прямо с того места, где началась проверка.595596**`(?!...)`**597598Отрицательная опережающая проверка. Это противоположность положительной проверке; она успешна, если вложенное выражение *не* совпадает в текущей позиции строки.599600Чтобы сделать это конкретным, рассмотрим случай, где опережающая проверка полезна. Рассмотрим простой шаблон для сопоставления имени файла и разделения его на базовое имя и расширение, разделённых точкой `.`. Например, в `news.rc` `news` – это базовое имя, а `rc` – расширение файла.601602Шаблон для сопоставления этого довольно прост:603604`.*[.].*$`605606Обратите внимание, что `.` нужно обрабатывать особым образом, потому что это метасимвол; я поместил его внутрь символьного класса. Также обратите внимание на завершающий `$`; он добавлен, чтобы гарантировать, что вся остальная часть строки должна быть включена в расширение. Это регулярное выражение совпадает с `foo.bar` и `autoexec.bat`, и `sendmail.cf`, и `printers.conf`.607608Теперь усложним задачу: что, если нужно сопоставить имена файлов, расширение которых не равно `bat`? Некоторые неверные попытки:609610`.*[.][^b].*$` Первая попытка выше пытается исключить `bat`, требуя, чтобы первый символ расширения не был `b`. Это неправильно, потому что шаблон также не соответствует `foo.bar`.611612`.*[.]([^b]..|.[^a].|..[^t])$`613614Выражение становится более запутанным, если попытаться исправить первое решение, потребовав совпадения одного из следующих случаев: первый символ расширения не `b`; второй символ не `a`; или третий символ не `t`. Это принимает `foo.bar` и отвергает `autoexec.bat`, но требует трёхбуквенного расширения и не принимает имя файла с двухбуквенным расширением, например `sendmail.cf`. Мы снова усложним шаблон, пытаясь это исправить.615616`.*[.]([^b].?.?|.[^a]?.?|..?[^t]?)$`617618В третьей попытке вторая и третья буквы сделаны необязательными, чтобы разрешить сопоставление расширений короче трёх символов, например `sendmail.cf`.619620Шаблон становится очень сложным, что затрудняет его чтение и понимание. Хуже того, если задача изменится и потребуется исключить как `bat`, так и `exe` в качестве расширений, шаблон станет ещё более запутанным и сложным.621622Отрицательная опережающая проверка разрешает всю эту путаницу:623624`.*[.](?!bat$)[^.]*$` Отрицательная опережающая проверка означает: если выражение `bat` не совпадает в этой точке, попробуйте оставшуюся часть шаблона; если `bat$` совпадает, весь шаблон не сработает. Замыкающий `$` требуется, чтобы гарантировать, что нечто вроде `sample.batch`, где расширение начинается только с `bat`, будет разрешено. `[^.]*` гарантирует, что шаблон работает, когда в имени файла есть несколько точек.625626Исключить другое расширение файла теперь легко: просто добавьте его как альтернативу внутри проверки. Следующий шаблон исключает имена файлов, которые заканчиваются на `bat` или `exe`:627628`.*[.](?!bat$|exe$)[^.]*$`629630## Изменение строк631632До этого момента мы просто выполняли поиск в статической строке. Регулярные выражения также часто используются для изменения строк различными способами с помощью следующих методов шаблона:633634| Метод/Атрибут | Назначение |635| --- | --- |636| `split()` | Разделяет строку на список, разбивая её везде, где совпадает RE |637| `sub()` | Находит все подстроки, совпадающие с RE, и заменяет их на другую строку |638| `subn()` | Делает то же самое, что `sub()`, но возвращает новую строку и количество замен |639640### Разделение строк641642Метод `split()` шаблона разбивает строку везде, где совпадает регулярное выражение, возвращая список фрагментов. Он похож на метод `split()` строк, но обеспечивает гораздо большую общность в разделителях, по которым можно выполнять разбиение; `split()` поддерживает разбиение только по пробельным символам или по фиксированной строке. Как и следовало ожидать, существует и функция уровня модуля [`re.split()`](https://python-all.ru/2.7/library/re.html#re.split).643644#### `.split(string[, maxsplit=0])`645646Разделяет *строку* по совпадениям регулярного выражения. Если в RE используются захватывающие круглые скобки, то их содержимое также будет возвращено как часть результирующего списка. Если *maxsplit* не равно нулю, выполняется не более *maxsplit* разбиений.647648Количество выполняемых разбиений можно ограничить, передав значение для *maxsplit*. Если *maxsplit* не равно нулю, будет выполнено не более *maxsplit* разбиений, а остаток строки возвращается как последний элемент списка. В следующем примере разделителем является любая последовательность неалфавитно-цифровых символов.649650```python651>>> p = re.compile(r'\W+')652>>> p.split('This is a test, short and sweet, of split().')653['This', 'is', 'a', 'test', 'short', 'and', 'sweet', 'of', 'split', '']654>>> p.split('This is a test, short and sweet, of split().', 3)655['This', 'is', 'a', 'test, short and sweet, of split().']656```657658Иногда нужно знать не только текст между разделителями, но и сам разделитель. Если в RE используются захватывающие круглые скобки, то их значения также возвращаются как часть списка. Сравните следующие вызовы:659660```python661>>> p = re.compile(r'\W+')662>>> p2 = re.compile(r'(\W+)')663>>> p.split('This... is a test.')664['This', 'is', 'a', 'test', '']665>>> p2.split('This... is a test.')666['This', '... ', 'is', ' ', 'a', ' ', 'test', '.', '']667```668669Функция уровня модуля [`re.split()`](https://python-all.ru/2.7/library/re.html#re.split) добавляет RE, используемый как первый аргумент, но в остальном идентична.670671```python672>>> re.split('[\W]+', 'Words, words, words.')673['Words', 'words', 'words', '']674>>> re.split('([\W]+)', 'Words, words, words.')675['Words', ', ', 'words', ', ', 'words', '.', '']676>>> re.split('[\W]+', 'Words, words, words.', 1)677['Words', 'words, words.']678```679680### Поиск и замена681682Ещё одна распространённая задача – найти все совпадения с шаблоном и заменить их другой строкой. Метод `sub()` принимает значение замены, которое может быть строкой или функцией, и обрабатываемую строку.683684#### `.sub(replacement, string[, count=0])`685686Возвращает строку, полученную заменой самых левых непересекающихся совпадений RE в *строке* на замену *replacement*. Если шаблон не найден, *строка* возвращается без изменений.687688Необязательный аргумент *count* – максимальное количество заменяемых вхождений шаблона; *count* должно быть неотрицательным целым числом. Значение по умолчанию 0 означает замену всех вхождений.689690Вот простой пример использования метода `sub()`. Он заменяет названия цветов словом `colour`:691692```python693>>> p = re.compile('(blue|white|red)')694>>> p.sub('colour', 'blue socks and red shoes')695'colour socks and colour shoes'696>>> p.sub('colour', 'blue socks and red shoes', count=1)697'colour socks and red shoes'698```699700Метод `subn()` делает то же самое, но возвращает кортеж из двух элементов, содержащий новую строку и количество выполненных замен:701702```python703>>> p = re.compile('(blue|white|red)')704>>> p.subn('colour', 'blue socks and red shoes')705('colour socks and colour shoes', 2)706>>> p.subn('colour', 'no colours at all')707('no colours at all', 0)708```709710Пустые совпадения заменяются только тогда, когда они не примыкают к предыдущему совпадению.711712```python713>>> p = re.compile('x*')714>>> p.sub('-', 'abxd')715'-a-b-d-'716```717718Если *replacement* является строкой, все escape-последовательности с обратной косой чертой обрабатываются. То есть `\n` преобразуется в символ новой строки, `\r` – в возврат каретки и так далее. Неизвестные escape-последовательности, такие как `\j`, остаются без изменений. Обратные ссылки, например `\6`, заменяются подстрокой, совпавшей с соответствующей группой в регулярном выражении. Это позволяет включать части исходного текста в результирующую строку замены.719720В этом примере ищется слово `section`, за которым следует строка, заключённая в `{`, `}`, и `section` заменяется на `subsection`:721722```python723>>> p = re.compile('section{ ( [^}]* ) }', re.VERBOSE)724>>> p.sub(r'subsection{\1}','section{First} section{second}')725'subsection{First} subsection{second}'726```727728Существует также синтаксис для ссылки на именованные группы, определённые с помощью синтаксиса `(?P<name>...)`. `\g<name>` будет использовать подстроку, совпавшую с группой с именем `name`, а `\g<number>` использует соответствующий номер группы. Таким образом, `\g<2>` эквивалентно `\2`, но не является неоднозначным в строке замены, такой как `\g<2>0`. (`\20` было бы интерпретировано как ссылка на группу 20, а не как ссылка на группу 2, за которой следует литеральный символ `'0'`.) Следующие подстановки эквивалентны, но используют все три варианта строки замены.729730```python731>>> p = re.compile('section{ (?P<name> [^}]* ) }', re.VERBOSE)732>>> p.sub(r'subsection{\1}','section{First}')733'subsection{First}'734>>> p.sub(r'subsection{\g<1>}','section{First}')735'subsection{First}'736>>> p.sub(r'subsection{\g<name>}','section{First}')737'subsection{First}'738```739740*replacement* также может быть функцией, что даёт ещё больше контроля. Если *replacement* является функцией, она вызывается для каждого непересекающегося вхождения *pattern*. При каждом вызове функции передаётся аргумент [объект совпадения](https://python-all.ru/2.7/library/re.html#match-objects) и она может использовать эту информацию для вычисления нужной строки замены и возврата её.741742В следующем примере функция замены преобразует десятичные числа в шестнадцатеричные:743744```python745>>> def hexrepl(match):746... "Return the hex string for a decimal number"747... value = int(match.group())748... return hex(value)749...750>>> p = re.compile(r'\d+')751>>> p.sub(hexrepl, 'Call 65490 for printing, 49152 for user code.')752'Call 0xffd2 for printing, 0xc000 for user code.'753```754755При использовании функции уровня модуля [`re.sub()`](https://python-all.ru/2.7/library/re.html#re.sub) шаблон передаётся как первый аргумент. Шаблон может быть предоставлен как объект или как строка; если нужно указать флаги регулярного выражения, необходимо либо использовать объект шаблона в качестве первого параметра, либо использовать встроенные модификаторы в строке шаблона, например `sub("(?i)b+", "x", "bbbb BBBB")` возвращает `'x x'`.756757## Распространенные проблемы758759Регулярные выражения – мощный инструмент для некоторых приложений, но в некоторых аспектах их поведение не интуитивно и иногда они ведут себя не так, как можно было бы ожидать. В этом разделе будут указаны некоторые наиболее распространённые ошибки.760761### Применение строковых методов762763Иногда использование модуля [`re`](https://python-all.ru/2.7/library/re.html#module-re) является ошибкой. Если производится сопоставление с фиксированной строкой или одиночным классом символов и не используются никакие возможности [`re`](https://python-all.ru/2.7/library/re.html#module-re), такие как флаг `IGNORECASE`, то вся мощь регулярных выражений может не понадобиться. Строки имеют несколько методов для выполнения операций с фиксированными строками, и они обычно намного быстрее, поскольку реализация представляет собой один небольшой цикл на C, оптимизированный для этой цели, а не большой, более обобщённый движок регулярных выражений.764765Примером может быть замена одной фиксированной строки на другую; например, можно заменить `word` на `deed`. `re.sub()` кажется подходящей функцией для этого, но рассмотрите метод `replace()`. Обратите внимание, что `replace()` также заменит `word` внутри слов, превратив `swordfish` в `sdeedfish`, но наивное RE `word` сделало бы то же самое. (Чтобы избежать замены в частях слов, шаблон должен быть `\bword\b`, чтобы требовать, чтобы `word` имело границу слова с обеих сторон. Это выходит за рамки возможностей `replace()`.)766767Ещё одна распространённая задача – удаление всех вхождений одного символа из строки или замена его другим символом. Можно сделать это с помощью чего-то вроде `re.sub('\n', ' ', S)`, но `translate()` способен выполнить обе задачи и будет быстрее любой операции с регулярными выражениями.768769Короче говоря, прежде чем обращаться к модулю [`re`](https://python-all.ru/2.7/library/re.html#module-re), подумайте, не решается ли ваша задача более быстрым и простым строковым методом.770771### match() и search()772773Функция `match()` проверяет совпадение только в начале строки, тогда как `search()` просматривает строку в поисках совпадения. Важно помнить об этом различии. Помните: `match()` сообщит об успешном совпадении, только если оно начинается с 0; если совпадение начинается не с нуля, `match()` *не* сообщит о нём.774775```python776>>> print re.match('super', 'superstition').span()777(0, 5)778>>> print re.match('super', 'insuperable')779None780```781782С другой стороны, `search()` будет просматривать строку и сообщать о первом найденном совпадении.783784```python785>>> print re.search('super', 'superstition').span()786(0, 5)787>>> print re.search('super', 'insuperable').span()788(2, 7)789```790791Иногда возникает соблазн продолжать использовать [`re.match()`](https://python-all.ru/2.7/library/re.html#re.match) и просто добавлять `.*` в начало RE. Сопротивляйтесь этому соблазну и используйте [`re.search()`](https://python-all.ru/2.7/library/re.html#re.search). Компилятор регулярных выражений выполняет некоторый анализ RE, чтобы ускорить процесс поиска совпадения. Один из таких анализов определяет, каким должен быть первый символ совпадения; например, шаблон, начинающийся с `Crow`, должен совпадать с `'C'`. Анализ позволяет движку быстро просматривать строку в поисках начального символа, пробуя полное совпадение только если найден `'C'`.792793Добавление `.*` сводит на нет эту оптимизацию, требуя сканирования до конца строки и последующего возврата для поиска совпадения для остальной части RE. Используйте [`re.search()`](https://python-all.ru/2.7/library/re.html#re.search) вместо этого.794795### Жадный и нежадный режимы796797При повторении регулярного выражения, как в `a*`, результатом является потребление как можно большей части шаблона. Этот факт часто подводит, когда пытаются сопоставить пару сбалансированных разделителей, например, угловые скобки, окружающие HTML-тег. Наивный шаблон для сопоставления одного HTML-тега не работает из-за жадной природы `.*`.798799```python800>>> s = '<html><head><title>Title</title>'801>>> len(s)80232803>>> print re.match('<.*>', s).span()804(0, 32)805>>> print re.match('<.*>', s).group()806<html><head><title>Title</title>807```808809RE находит `'<'` в `<html>`, а `.*` поглощает остаток строки. Однако в RE остаётся ещё часть, и `>` не может совпасть в конце строки, поэтому движку регулярных выражений приходится возвращаться посимвольно, пока он не найдёт совпадение для `>`. Итоговое совпадение простирается от `'<'` в `<html>` до `'>'` в `</title>`, что не то, что нужно.810811В этом случае решение – использовать нежадные квантификаторы `*?`, `+?`, `??` или `{m,n}?`, которые захватывают как *мало* текста, насколько это возможно. В приведённом выше примере `'>'` проверяется сразу после совпадения первого `'<'`, и когда это не удаётся, движок продвигается на один символ, заново проверяя `'>'` на каждом шаге. Это даёт правильный результат:812813```python814>>> print re.match('<.*?>', s).group()815<html>816```817818(Заметьте, что разбор HTML или XML с помощью регулярных выражений – дело мучительное. Быстрые и грязные шаблоны справятся с обычными случаями, но у HTML и XML есть особые случаи, которые сломают очевидное регулярное выражение; к тому времени, как вы напишете регулярное выражение, обрабатывающее все возможные случаи, шаблоны станут *очень* сложными. Используйте для таких задач модуль разбора HTML или XML.)819820### Использование re.VERBOSE821822К этому моменту вы, вероятно, заметили, что регулярные выражения – это очень компактная запись, но они не слишком читаемы. RE умеренной сложности могут превратиться в длинные наборы обратных слешей, скобок и метасимволов, что затрудняет их чтение и понимание.823824Для таких RE указание флага `re.VERBOSE` при компиляции регулярного выражения может быть полезно, поскольку позволяет форматировать регулярное выражение более чётко.825826Флаг `re.VERBOSE` имеет несколько эффектов. Пробельные символы в регулярном выражении, которые *не находятся* внутри символьного класса, игнорируются. Это означает, что выражение вида `dog | cat` эквивалентно менее читаемому `dog|cat`, но `[a b]` по-прежнему будет соответствовать символам `'a'`, `'b'` или пробелу. Кроме того, в RE можно вставлять комментарии; комментарии начинаются с символа `#` и продолжаются до следующей новой строки. При использовании с тройными кавычками это позволяет форматировать RE более аккуратно:827828```python829pat = re.compile(r"""830 \s* # Skip leading whitespace831 (?P<header>[^:]+) # Header name832 \s* : # Whitespace, and a colon833 (?P<value>.*?) # The header's value -- *? used to834 # lose the following trailing whitespace835 \s*$ # Trailing whitespace to end-of-line836""", re.VERBOSE)837```838839Это гораздо читаемее, чем:840841```python842pat = re.compile(r"\s*(?P<header>[^:]+)\s*:(?P<value>.*?)\s*$")843```844845## Обратная связь846847Регулярные выражения – сложная тема. Помог ли вам этот документ понять их? Были ли части, которые остались неясными, или проблемы, с которыми вы столкнулись и которые не были здесь рассмотрены? Если да, отправьте предложения по улучшению автору.848849Наиболее полной книгой по регулярным выражениям, пожалуй, является \*Mastering Regular Expressions\* Джеффри Фридла, издательство O’Reilly. К сожалению, она сосредоточена исключительно на разновидностях регулярных выражений Perl и Java и вообще не содержит материала по Python, так что она не пригодится в качестве справочника по программированию на Python. (В первом издании рассматривался модуль `regex`, который теперь удалён из Python, что вам не сильно поможет.) Рекомендуем взять её в библиотеке.850851Сноски852853**[1](https://python-all.ru/2.7/howto/regex.html#id1)**854855Введено в Python 2.2.2.856