Документация Python неофициальный перевод

regex.md

956 строк · 89.6 КБ · обычная страница · сырой текст · скачать

1> **Источник:** https://python-all.ru/3.0/howto/regex.html2>3> «Документация Python на русском» – неофициальный перевод официальной документации Python: версии от 2.6 до 3.16, полнотекстовый поиск, английский оригинал рядом с переводом. Эта Markdown-версия страницы предназначена для работы с LLM: вставьте её в ChatGPT, Claude или Cursor.45---67# Регулярные выражения: руководство89| Автор: | A.M. Kuchling |10| --- | --- |11| Версия: | 0.05 |1213Аннотация1415Этот документ представляет собой вводное руководство по использованию регулярных выражений в Python с модулем [`re`](https://python-all.ru/3.0/library/re.html#module-re). Он даёт более мягкое введение, чем соответствующий раздел в справочнике библиотеки.1617## Введение1819Модуль [`re`](https://python-all.ru/3.0/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/3.0/library/re.html#module-re). С помощью этого маленького языка задаются правила для множества возможных строк, которые требуется сопоставить; это множество может содержать английские предложения, адреса электронной почты, команды TeX или что угодно. Затем можно задавать вопросы: «Соответствует ли эта строка шаблону?», или «Есть ли совпадение с шаблоном где-нибудь в этой строке?». Также можно использовать RE для изменения строки или её разделения различными способами.2223Шаблоны регулярных выражений компилируются в набор байткодов, которые затем выполняются механизмом сопоставления, написанным на C. Для продвинутого использования может потребоваться тщательно следить за тем, как механизм будет выполнять данное RE, и писать RE определённым образом, чтобы получить байткод, работающий быстрее. Оптимизация не рассматривается в этом документе, поскольку она требует хорошего понимания внутреннего устройства механизма сопоставления.2425Язык регулярных выражений относительно мал и ограничен, поэтому не все задачи обработки строк можно решить с помощью регулярных выражений. Существуют также задачи, которые *можно* выполнить с помощью регулярных выражений, но выражения оказываются очень сложными. В таких случаях, возможно, лучше написать код на Python для обработки; хотя код на Python будет медленнее, чем сложное регулярное выражение, он, вероятно, будет более понятным.2627## Простые шаблоны2829Начнём с изучения самых простых регулярных выражений. Поскольку регулярные выражения используются для работы со строками, начнём с самой распространённой задачи: сопоставления символов.3031Подробное объяснение теоретических основ регулярных выражений (детерминированные и недетерминированные конечные автоматы) можно найти почти в любом учебнике по написанию компиляторов.3233### Сопоставление символов3435Большинство букв и символов просто совпадают сами с собой. Например, регулярное выражение `test` точно совпадёт со строкой `test`. (Можно включить режим без учёта регистра, который позволит этому RE совпадать с `Test` или `TEST` ; подробнее об этом позже.)3637Из этого правила есть исключения: некоторые символы являются специальными *метасимволами* и не сопоставляются сами с собой. Вместо этого они указывают, что должно быть сопоставлено что-то необычное, или влияют на другие части РВ, повторяя их или меняя их значение. Значительная часть этого документа посвящена обсуждению различных метасимволов и их действий.3839Вот полный список метасимволов; их значения будут рассмотрены в остальной части этого HOWTO.4041```python42. ^ $ * + ? { [ ] \ | ( )43```4445Первые метасимволы, которые мы рассмотрим, – это `[` и `]`. Они используются для указания класса символов – набора символов, которые требуется сопоставить. Символы можно перечислить по отдельности, или же диапазон символов можно указать, задав два символа и разделив их `'-'`. Например, `[abc]` совпадёт с любым из символов `a`, `b` или `c`; это то же самое, что `[a-c]`, где используется диапазон для задания того же набора символов. Если требуется сопоставить только строчные буквы, RE будет `[a-z]`.4647Внутри классов метасимволы не активны. Например, `[akm$]` совпадёт с любым из символов `'a'`, `'k'`, `'m'` или `'$'`; обычно `'$'` является метасимволом, но внутри класса символов он теряет свою особую природу.4849Вы можете сопоставить символы, не перечисленные в классе, *дополнив* набор. Это обозначается включением `'^'` в качестве первого символа класса; `'^'` вне символьного класса просто соответствует `'^'` символу. Например, `[^5]` будет соответствовать любому символу, кроме `'5'`.5051Пожалуй, самый важный метасимвол – обратная косая черта, `\`. Как и в строковых литералах Python, за обратной косой чертой могут следовать различные символы для обозначения специальных последовательностей. Она также используется для экранирования всех метасимволов, чтобы их можно было сопоставлять в шаблонах; например, если нужно сопоставить `[` или `\`, можно поставить перед ними обратную косую черту, чтобы убрать их специальное значение: `\[` или `\\`.5253Некоторые специальные последовательности, начинающиеся с `'\'`, представляют предопределённые наборы символов, которые часто бывают полезны, например, набор цифр, набор букв или набор любых символов, не являющихся пробельными. Доступны следующие предопределённые специальные последовательности:5455**`\d`**5657Соответствует любой десятичной цифре; эквивалентно классу5859`[0-9]`6061.6263**`\D`**6465Соответствует любому нецифровому символу; эквивалентно классу6667`[^0-9]`6869.7071**`\s`**7273Совпадает с любым пробельным символом; это эквивалентно классу7475`[ \t\n\r\f\v]`7677.7879**`\S`**8081Совпадает с любым непробельным символом; это эквивалентно классу8283`[^ \t\n\r\f\v]`8485.8687**`\w`**8889Совпадает с любым буквенно-цифровым символом; это эквивалентно классу9091`[a-zA-Z0-9_]`9293.9495**`\W`**9697Совпадает с любым не буквенно-цифровым символом; это эквивалентно классу9899`[^a-zA-Z0-9_]`100101.102103Эти последовательности можно включать внутрь класса символов. Например, `[\s,.]` – это класс символов, который совпадёт с любым пробельным символом, или `','`, или `'.'`.104105Последний метасимвол в этом разделе – `.`. Он совпадает с любым символом, кроме символа новой строки, и существует альтернативный режим (`re.DOTALL`), в котором он будет совпадать даже с символом новой строки. `'.'` часто используется, когда требуется сопоставить «любой символ».106107### Повторяющиеся элементы108109Возможность сопоставлять различные наборы символов – это первое, что регулярные выражения умеют делать, чего уже нельзя сделать с помощью методов строк. Однако если бы это была единственная дополнительная возможность регулярных выражений, они не были бы большим достижением. Ещё одна возможность – указывать, что части РВ должны повторяться определённое количество раз.110111Первый метасимвол для повторения, который мы рассмотрим, – это `*`. `*` не соответствует литеральному символу `*`; вместо этого он указывает, что предыдущий символ может быть сопоставлен ноль или более раз, а не ровно один раз.112113Например, `ca*t` найдет совпадение с `ct` (0 символов `a`), `cat` (1 символ `a`), `caaat` (3 символа `a`) и так далее. Движок регулярных выражений имеет различные внутренние ограничения, связанные с размером типа `int` в C, которые не позволят найти совпадение с более чем 2 миллиардами символов `a`; для построения такой длинной строки вряд ли хватит памяти, поэтому с этим ограничением вы не столкнетесь.114115Повторы, такие как `*`, являются *жадными*; при повторении RE движок сопоставления будет пытаться повторить его максимально возможное количество раз. Если последующие части шаблона не совпадают, движок сопоставления отступает назад и пробует снова с меньшим числом повторов.116117Пошаговый пример прояснит это. Рассмотрим выражение `a[bcd]*b`. Оно совпадает с буквой `'a'`, нулём или более букв из класса `[bcd]` и, наконец, заканчивается `'b'`. Представим сопоставление этого RE со строкой `abcbd`.118119| Шаг | Совпадение | Пояснение |120| --- | --- | --- |121| 1 | `a` | `a` в RE совпадает. |122| 2 | `abcbd` | Движок сопоставляет `[bcd]*`, заходя так далеко, как может, то есть до конца строки. |123| 3 | *Неудача* | Движок пытается сопоставить `b`, но текущая позиция находится в конце строки, поэтому это не удаётся. |124| 4 | `abcb` | Отступить, чтобы `[bcd]*` совпадал на один символ меньше. |125| 5 | *Неудача* | Попробовать `b` снова, но текущая позиция находится на последнем символе, которым является `'d'`. |126| 6 | `abc` | Снова отступить, чтобы `[bcd]*` совпадал только с `bc`. |127| 6 | `abcb` | Попробуйте `b` снова. На этот раз символ в текущей позиции – `'b'`, поэтому он совпадает. |128129Достигнут конец РВ, и оно совпало с `abcb`. Это показывает, как движок сопоставления сначала проходит максимально далеко, и если совпадение не найдено, он постепенно отступает и снова и снова пробует остаток РВ. Он будет отступать, пока не попробует ноль совпадений для `[bcd]*`, и если это в итоге не удаётся, движок делает вывод, что строка вообще не соответствует РВ.130131Ещё один повторяющийся метасимвол – `+`, который совпадает один или более раз. Обратите внимание на разницу между `*` и `+`; `*` совпадает *ноль* или более раз, поэтому повторяемый элемент может вообще отсутствовать, в то время как `+` требует как минимум *одного* вхождения. Для аналогичного примера, `ca+t` совпадёт с `cat` (1 `a`), `caaat` (3 `a`), но не совпадёт с `ct`.132133Есть ещё два повторяющихся квантификатора. Символ вопросительного знака, `?`, совпадает один раз или ноль раз; можно считать, что он помечает что-то как необязательное. Например, `home-?brew` совпадёт либо с `homebrew`, либо с `home-brew`.134135Самый сложный повторяющийся квантификатор – `{m,n}`, где *m* и *n* – десятичные целые числа. Этот квантификатор означает, что должно быть как минимум *m* повторений и не более *n*. Например, `a/{1,3}b` совпадёт с `a/b`, `a//b` и `a///b`. Он не совпадёт с `ab` (без слэшей) или с `a////b` (четыре слэша).136137Можно опустить либо *m*, либо *n*; в этом случае подразумевается разумное значение для отсутствующего параметра. Пропуск *m* интерпретируется как нижняя граница 0, а пропуск *n* приводит к верхней границе бесконечности – на самом деле верхняя граница – это упомянутый ранее предел в 2 миллиарда, но её можно считать бесконечностью.138139Можно заметить, что три остальных квантификатора можно выразить через эту нотацию. `{0,}` – то же, что `*`, `{1,}` эквивалентен `+`, а `{0,1}` – то же, что `?`. Лучше использовать `*`, `+` или `?`, когда это возможно, просто потому что они короче и легче читаются.140141## Использование регулярных выражений142143Теперь, когда мы рассмотрели несколько простых регулярных выражений, как же их использовать в Python? Модуль [`re`](https://python-all.ru/3.0/library/re.html#module-re) предоставляет интерфейс к движку регулярных выражений, позволяя компилировать РВ в объекты и затем выполнять с ними сопоставление.144145### Компиляция регулярных выражений146147Регулярные выражения компилируются в экземпляры `RegexObject`, которые имеют методы для различных операций, таких как поиск совпадений с шаблоном или выполнение подстановок в строках.148149```python150>>> import re151>>> p = re.compile('ab*')152>>> p153<re.RegexObject instance at 80b4150>154```155156[`re.compile()`](https://python-all.ru/3.0/library/re.html#re.compile) также принимает необязательный аргумент *flags*, используемый для включения различных специальных возможностей и синтаксических вариаций. Мы рассмотрим доступные настройки позже, а пока достаточно одного примера:157158```python159>>> p = re.compile('ab*', re.IGNORECASE)160```161162РВ передаётся в [`re.compile()`](https://python-all.ru/3.0/library/re.html#re.compile) в виде строки. РВ обрабатываются как строки, потому что регулярные выражения не являются частью основного языка Python, и для их записи не было создано специального синтаксиса. (Есть приложения, которым РВ вообще не нужны, так что нет необходимости раздувать спецификацию языка, включая их.) Вместо этого модуль [`re`](https://python-all.ru/3.0/library/re.html#module-re) – это просто модуль-расширение на C, входящий в состав Python, как и модули [`socket`](https://python-all.ru/3.0/library/socket.html#module-socket) или [`zlib`](https://python-all.ru/3.0/library/zlib.html#module-zlib).163164Размещение RE в строках сохраняет язык Python более простым, но имеет один недостаток, который является темой следующего раздела.165166### Проблема обратной косой черты167168Как было сказано ранее, регулярные выражения используют символ обратной косой черты (`'\'`) для обозначения специальных форм или для использования специальных символов без их специального значения. Это конфликтует с использованием того же символа в строковых литералах Python для той же цели.169170Допустим, нужно написать РВ, которое совпадает со строкой `\section`, которая может встречаться в файле LaTeX. Чтобы понять, что писать в коде программы, начните с нужной строки для сопоставления. Затем нужно экранировать все обратные слэши и другие метасимволы, предваряя их обратным слэшем, в результате получится строка `\\section`. Результирующая строка, которую нужно передать в [`re.compile()`](https://python-all.ru/3.0/library/re.html#re.compile), должна быть `\\section`. Однако, чтобы выразить это в виде строкового литерала Python, оба обратных слэша необходимо экранировать *снова*.171172| Символы | Этап |173| --- | --- |174| `\section` | Строка для совпадения |175| `\\section` | Экранированный обратный слэш для [`re.compile()`](https://python-all.ru/3.0/library/re.html#re.compile) |176| `"\\\\section"` | Экранированные обратные слеши для строкового литерала |177178Короче говоря, для сопоставления с литералом обратной косой черты нужно записать `'\\\\'` в качестве строки RE, потому что регулярное выражение должно быть `\\`, а каждый обратный слеш должен быть представлен как `\\` внутри обычного строкового литерала Python. В RE, где обратные слеши встречаются многократно, это приводит к множеству повторных слешей и делает итоговые строки трудными для понимания.179180Решение – использовать в Python сырые строки для регулярных выражений; обратные слэши не обрабатываются особым образом в строковом литерале с префиксом `'r'`, поэтому `r"\n"` – это двухсимвольная строка, содержащая `'\'` и `'n'`, в то время как `"\n"` – односимвольная строка, содержащая символ новой строки. Регулярные выражения часто записываются в коде Python с использованием этой нотации сырых строк.181182| Обычная строка | Сырая строка |183| --- | --- |184| `"ab*"` | `r"ab*"` |185| `"\\\\section"` | `r"\\section"` |186| `"\\w+\\s+\\1"` | `r"\w+\s+\1"` |187188### Выполнение поиска совпадений189190Когда у вас есть объект, представляющий скомпилированное регулярное выражение, что с ним делать? Экземпляры `RegexObject` имеют несколько методов и атрибутов. Здесь будут рассмотрены только самые важные; полный список можно найти в документации к модулю [`re`](https://python-all.ru/3.0/library/re.html#module-re).191192| Метод/Атрибут | Назначение |193| --- | --- |194| `match()` | Определяет, совпадает ли RE в начале строки. |195| `search()` | Сканирует строку в поисках любого места, где данное регулярное выражение совпадает. |196| `findall()` | Находит все подстроки, совпадающие с регулярным выражением, и возвращает их в виде списка. |197| `finditer()` | Find all substrings where the RE matches, and returns them as an [*iterator*](https://python-all.ru/3.0/glossary.html#term-iterator). |198199`match()` и `search()` возвращают `None`, если совпадение не найдено. Если совпадение найдено, возвращается экземпляр `MatchObject`, содержащий информацию о совпадении: где оно начинается и заканчивается, какую подстроку захватило и т.д.200201Изучить это можно, интерактивно экспериментируя с модулем [`re`](https://python-all.ru/3.0/library/re.html#module-re). Если у вас установлен Tkinter, можно также посмотреть на `Tools/scripts/redemo.py` – демонстрационную программу, включённую в дистрибутив Python. Она позволяет вводить регулярные выражения и строки, а также показывает, соответствует ли RE строке или нет. `redemo.py` может быть весьма полезен при отладке сложных RE. [Kodos](https://python-all.ru/3.0/howto/regex.html) от Фила Шварца – также интерактивный инструмент для разработки и тестирования RE-шаблонов.202203В этом HOWTO используется стандартный интерпретатор Python для примеров. Сначала запустите интерпретатор Python, импортируйте модуль [`re`](https://python-all.ru/3.0/library/re.html#module-re) и скомпилируйте РВ:204205```python206Python 2.2.2 (#1, Feb 10 2003, 12:57:01)207>>> import re208>>> p = re.compile('[a-z]+')209>>> p210<_sre.SRE_Pattern object at 80c3c28>211```212213Теперь можно попробовать сопоставить различные строки с РВ `[a-z]+`. Пустая строка вообще не должна совпадать, так как `+` означает «одно или более повторений». `match()` в этом случае должна вернуть `None`, что приведёт к тому, что интерпретатор не выведет ничего. Можно явно напечатать результат `match()`, чтобы это стало очевидно.214215```python216>>> p.match("")217>>> print(p.match(""))218None219```220221Теперь попробуем применить его к строке, которая должна совпадать, например, `tempo`. В этом случае `match()` вернёт `MatchObject`, поэтому результат следует сохранить в переменной для последующего использования.222223```python224>>> m = p.match('tempo')225>>> m226<_sre.SRE_Match object at 80c4f68>227```228229Теперь можно запросить у `MatchObject` информацию о совпавшей строке. Экземпляры `MatchObject` также имеют несколько методов и атрибутов; наиболее важные из них: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()` возвращает подстроку, совпавшую с регулярным выражением. `start()` и `end()` возвращают начальный и конечный индексы совпадения. `span()` возвращает оба индекса в одном кортеже. Поскольку метод `match()` проверяет совпадение только в начале строки, `start()` всегда будет равен нулю. Однако метод `search()` экземпляров `RegexObject` сканирует всю строку, поэтому в этом случае совпадение может начинаться не с нуля.250251```python252>>> print(p.match('::: message'))253None254>>> m = p.search('::: message') ; print(m)255<re.MatchObject instance at 80c9650>256>>> m.group()257'message'258>>> m.span()259(4, 11)260```261262В реальных программах обычно сохраняют `MatchObject` в переменной, а затем проверяют, не равно ли значение `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Два метода `RegexObject` возвращают все совпадения для шаблона. `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()` возвращает последовательность экземпляров `MatchObject` в виде [*итератора*](https://python-all.ru/3.0/glossary.html#term-iterator). [\[1\]](https://python-all.ru/3.0/howto/regex.html#id3)282283```python284>>> iterator = p.finditer('12 drummers drumming, 11 ... 10 ...')285>>> iterator286<callable-iterator object at 0x401833ac>287>>> for match in iterator:288...     print(match.span())289...290(0, 2)291(22, 24)292(29, 31)293```294295### Функции уровня модуля296297Необязательно создавать `RegexObject` и вызывать его методы; модуль [`re`](https://python-all.ru/3.0/library/re.html#module-re) также предоставляет функции верхнего уровня: `match()`, `search()`, `findall()`, `sub()` и так далее. Эти функции принимают те же аргументы, что и соответствующий метод `RegexObject`, только строка RE добавляется как первый аргумент, и они по-прежнему возвращают либо `None`, либо экземпляр `MatchObject`.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<re.MatchObject instance at 80c5978>304```305306Под капотом эти функции просто создают `RegexObject` и вызывают на нём соответствующий метод. Они также сохраняют скомпилированный объект в кеше, поэтому последующие вызовы с тем же RE выполняются быстрее.307308Стоит ли использовать функции уровня модуля или лучше получить `RegexObject` и вызывать его методы самостоятельно? Этот выбор зависит от того, как часто будет использоваться RE, и от вашего стиля кодирования. Если RE используется только в одном месте кода, то, вероятно, удобнее модульные функции. Если программа содержит много регулярных выражений или переиспользует одни и те же в разных местах, то имеет смысл собрать все определения в одном месте, в участке кода, который компилирует все RE заранее. В качестве примера из стандартной библиотеки – вот выдержка из ныне устаревшего `xmllib.py`:309310```python311ref = re.compile( ... )312entityref = re.compile( ... )313charref = re.compile( ... )314starttagopen = re.compile( ... )315```316317Я обычно предпочитаю работать со скомпилированным объектом, даже для одноразового использования, но мало кто будет таким же пуристом в этом вопросе, как я.318319### Флаги компиляции320321Флаги компиляции позволяют изменять некоторые аспекты работы регулярных выражений. Флаги доступны в модуле [`re`](https://python-all.ru/3.0/library/re.html#module-re) под двумя именами: длинным, например `IGNORECASE`, и коротким, однобуквенным, например `I`. (Если вы знакомы с модификаторами шаблонов в Perl, то однобуквенные формы используют те же буквы; например, короткая форма [`re.VERBOSE`](https://python-all.ru/3.0/library/re.html#re.VERBOSE) – это [`re.X`](https://python-all.ru/3.0/library/re.html#re.X).) Несколько флагов можно задать с помощью побитового ИЛИ: например, `re.I | re.M` устанавливает оба флага `I` и `M`.322323Ниже приведена таблица доступных флагов, а затем более подробное описание каждого из них.324325| Флаг | Значение |326| --- | --- |327| `DOTALL`, `S` | Заставляет `.` соответствовать любому символу, включая символы новой строки |328| `IGNORECASE`, `I` | Выполняет регистронезависимые сопоставления |329| `LOCALE`, `L` | Выполняет сопоставление с учётом локали |330| `MULTILINE`, `M` | Многострочное сопоставление, затрагивающее `^` и `$` |331| `VERBOSE`, `X` | Включает подробные (verbose) регулярные выражения, которые можно организовать более чисто и понятно. |332333#### `I`334335#### `IGNORECASE`336337Выполняет сопоставление без учёта регистра; классы символов и строковые литералы будут сопоставляться с буквами, игнорируя регистр. Например,338339`[A-Z]`340341будет также находить строчные буквы, а342343`Spam`344345будет соответствовать346347`Spam`348349,350351`spam`352353или354355`spAM`356357. Это приведение к нижнему регистру не учитывает текущую локаль; оно будет учитывать, если также установить флаг358359`LOCALE`360361.362363#### `L`364365#### `LOCALE`366367Делает `\w`, `\W`, `\b` и `\B` зависимыми от текущей локали.368369Локали – это возможность библиотеки C, предназначенная для написания программ, учитывающих языковые различия. Например, при обработке французского текста может понадобиться написать `\\w+` для поиска слов, но `\\w` соответствует только классу символов `[A-Za-z]`; он не будет находить `'é'` или `'ç'`. Если система настроена правильно и выбрана французская локаль, некоторые функции C сообщат программе, что `'é'` также следует считать буквой. Установка флага `LOCALE` при компиляции регулярного выражения заставит результирующий скомпилированный объект использовать эти функции C для `\\w`; это медленнее, но позволяет `\\w+` находить французские слова так, как и ожидается.370371#### `M`372373#### `MULTILINE`374375(`^` и `$` пока не объяснены; они будут представлены в разделе [*Дополнительные метасимволы*](https://python-all.ru/3.0/howto/regex.html#more-metacharacters).)376377Обычно `^` сопоставляется только в начале строки, а `$` – только в конце строки и непосредственно перед символом новой строки (если он есть) в конце строки. Когда указан этот флаг, `^` сопоставляется в начале строки и в начале каждой строки внутри строки, сразу после каждого символа новой строки. Аналогично, метасимвол `$` сопоставляется в конце строки и в конце каждой строки (непосредственно перед каждым символом новой строки).378379#### `S`380381#### `DOTALL`382383Заставляет специальный символ384385`'.'`386387сопоставляться с любым символом, включая символ новой строки; без этого флага388389`'.'`390391будет сопоставляться с любым символом,392393*кроме*394395символа новой строки.396397#### `X`398399#### `VERBOSE`400401Этот флаг позволяет писать более читаемые регулярные выражения, предоставляя больше гибкости в их форматировании. Когда этот флаг указан, пробельные символы внутри строки регулярного выражения игнорируются, за исключением случаев, когда пробел находится внутри символьного класса или перед ним стоит неэкранированная обратная косая черта; это позволяет более четко организовывать и расставлять отступы в регулярном выражении. Этот флаг также позволяет помещать комментарии внутри регулярного выражения, которые будут игнорироваться движком; комментарии обозначаются символом `'#'`, который не находится внутри символьного класса и не предваряется неэкранированной обратной косой чертой.402403Например, вот RE, использующий [`re.VERBOSE`](https://python-all.ru/3.0/library/re.html#re.VERBOSE); видите, насколько его легче читать?404405```python406charref = re.compile(r"""407 &[#]                # Start of a numeric entity reference408 (409     0[0-7]+         # Octal form410   | [0-9]+          # Decimal form411   | x[0-9a-fA-F]+   # Hexadecimal form412 )413 ;                   # Trailing semicolon414""", re.VERBOSE)415```416417Без режима verbose регулярное выражение выглядело бы так:418419```python420charref = re.compile("&#(0[0-7]+"421                     "|[0-9]+"422                     "|x[0-9a-fA-F]+);")423```424425В приведённом выше примере использовалась автоматическая конкатенация строковых литералов Python для разбиения RE на более мелкие части, но её по-прежнему сложнее понять, чем версию с [`re.VERBOSE`](https://python-all.ru/3.0/library/re.html#re.VERBOSE).426427## Дополнительные возможности шаблонов428429До сих пор мы рассмотрели только часть возможностей регулярных выражений. В этом разделе мы рассмотрим некоторые новые метасимволы и способы использования групп для извлечения частей сопоставленного текста.430431### Дополнительные метасимволы432433Остались некоторые метасимволы, которые мы ещё не рассмотрели. Большинство из них будет рассмотрено в этом разделе.434435Некоторые из оставшихся метасимволов, которые предстоит обсудить, – это *проверки с нулевой шириной* (zero-width assertions). Они не заставляют движок продвигаться по строке; вместо этого они вообще не потребляют символы и просто успешно проходят или нет. Например, `\\b` – это проверка того, что текущая позиция находится на границе слова; позиция `\\b` вообще не меняется. Это означает, что проверки с нулевой шириной никогда не следует повторять, потому что если они один раз совпадают в данной позиции, то, очевидно, могут совпадать бесконечное количество раз.436437**`|`**438439Альтернатива, или оператор «или». Если A и B – регулярные выражения, то `A|B` будет соответствовать любой строке, которая соответствует либо `A`, либо `B`. `|` имеет очень низкий приоритет, чтобы разумно работать при альтернативе многозначных строк. `Crow|Servo` будет соответствовать либо `Crow`, либо `Servo`, а не `Cro`, `'w'` или `'S'`, и `ervo`.440441Чтобы сопоставить литерал `'|'`, используйте `\|` или заключите его в символьный класс, как в `[|]`.442443**`^`**444445Соответствует началу строк. Если не установлен флаг `MULTILINE`, будет соответствовать только началу всей строки. В режиме `MULTILINE` также соответствует сразу после каждого символа новой строки внутри строки.446447Например, если нужно найти слово `From` только в начале строки, следует использовать RE `^From`.448449```python450>>> print(re.search('^From', 'From Here to Eternity'))451<re.MatchObject instance at 80c1520>452>>> print(re.search('^From', 'Reciting From Memory'))453None454```455456**`$`**457458Совпадение в конце строки, которая определяется как конец строки или любая позиция, за которой следует символ перевода строки.459460```python461>>> print(re.search('}$', '{block}'))462<re.MatchObject instance at 80adfa8>463>>> print(re.search('}$', '{block} '))464None465>>> print(re.search('}$', '{block}\n'))466<re.MatchObject instance at 80adfa8>467```468469Чтобы сопоставить литерал `'$'`, используйте `\$` или заключите его в символьный класс, как в `[$]`.470471**`\A`**472473Совпадает только в начале строки. Если не используется режим474475`MULTILINE`476477,478479`\A`480481и482483`^`484485по сути одно и то же. В режиме486487`MULTILINE`488489они различаются:490491`\A`492493по-прежнему совпадает только в начале строки, а494495`^`496497может совпадать в любом месте строки после символа новой строки.498499**`\Z`**500501Совпадение только в конце строки.502503**`\b`**504505Граница слова. Это утверждение нулевой длины, которое совпадает только в начале или конце слова. Слово определяется как последовательность буквенно-цифровых символов, поэтому конец слова обозначается пробелом или не-буквенно-цифровым символом.506507Следующий пример находит `class` только когда это целое слово; не будет находить, когда оно является частью другого слова.508509```python510>>> p = re.compile(r'\bclass\b')511>>> print(p.search('no class at all'))512<re.MatchObject instance at 80c8f28>513>>> print(p.search('the declassified algorithm'))514None515>>> print(p.search('one subclass is'))516None517```518519При использовании этой специальной последовательности нужно помнить о двух тонкостях. Первая: это самое серьёзное столкновение между строковыми литералами Python и последовательностями регулярных выражений. В строковых литералах Python `\b` – это символ backspace (ASCII 8). Если не используются сырые строки, Python преобразует `\b` в backspace, и регулярное выражение будет работать не так, как ожидается. Следующий пример выглядит так же, как предыдущее регулярное выражение, но перед строкой RE нет `'r'`.520521```python522>>> p = re.compile('\bclass\b')523>>> print(p.search('no class at all'))524None525>>> print(p.search('\b' + 'class' + '\b')  )526<re.MatchObject instance at 80c3ee0>527```528529Внутри символьного класса, где эта проверка не нужна, `\b` представляет символ backspace (для совместимости со строковыми литералами Python).530531**`\B`**532533Ещё одна проверка с нулевой длиной; противоположность534535`\b`536537. Совпадает, когда текущая позиция не находится на границе слова.538539### Группировка540541Часто требуется получить больше информации, чем просто – совпало RE или нет. Регулярные выражения часто используются для разбора строк с помощью RE, разделённого на несколько подгрупп, каждая из которых соответствует интересующему компоненту. Например, строка заголовка RFC-822 разделена на имя заголовка и значение, разделённые `':'`, так:542543```python544From: author@example.com545User-Agent: Thunderbird 1.5.0.9 (X11/20061227)546MIME-Version: 1.0547To: editor@example.com548```549550Это можно обработать, написав регулярное выражение, которое сопоставляет всю строку заголовка, и имеет одну группу для имени заголовка и другую – для значения заголовка.551552Группы выделяются метасимволами `'('` и `')'`. `'('` и `')'` имеют примерно тот же смысл, что и в математических выражениях: они группируют содержащиеся в них выражения, и содержимое группы можно повторять с помощью квантификаторов, таких как `*`, `+`, `?` или `{m,n}`. Например, `(ab)*` найдёт ноль или более повторений `ab`.553554```python555>>> p = re.compile('(ab)*')556>>> print(p.match('ababababab').span())557(0, 10)558```559560Группы, обозначенные с помощью `'('`, `')'`, также захватывают начальный и конечный индекс совпавшего текста; их можно получить, передав аргумент `group()`, `start()`, `end()` и `span()`. Группы нумеруются начиная с 0. Группа 0 всегда присутствует; это всё RE, поэтому методы `MatchObject` по умолчанию принимают группу 0 в качестве аргумента. Позже мы увидим, как задавать группы, которые не захватывают диапазон совпавшего текста.561562```python563>>> p = re.compile('(a)b')564>>> m = p.match('ab')565>>> m.group()566'ab'567>>> m.group(0)568'ab'569```570571Подгруппы нумеруются слева направо, начиная с 1. Группы могут быть вложенными; чтобы определить номер, нужно просто посчитать открывающие скобки, идя слева направо.572573```python574>>> p = re.compile('(a(b)c)d')575>>> m = p.match('abcd')576>>> m.group(0)577'abcd'578>>> m.group(1)579'abc'580>>> m.group(2)581'b'582```583584`group()` можно передать несколько номеров групп одновременно; в этом случае он вернёт кортеж с соответствующими значениями для этих групп.585586```python587>>> m.group(2,1,2)588('b', 'abc', 'b')589```590591Метод `groups()` возвращает кортеж, содержащий строки для всех подгрупп – от 1 до их общего количества.592593```python594>>> m.groups()595('abc', 'b')596```597598Обратные ссылки в шаблоне позволяют указать, что содержимое ранее захваченной группы должно также присутствовать в текущей позиции строки. Например, `\1` будет успешным, если точное содержимое группы 1 можно найти в текущей позиции, иначе – нет. Помните, что строковые литералы Python также используют обратную косую черту с числами для включения произвольных символов в строку, поэтому обязательно используйте сырую строку при включении обратных ссылок в RE.599600Например, следующее RE обнаруживает удвоенные слова в строке.601602```python603>>> p = re.compile(r'(\b\w+)\s+\1')604>>> p.search('Paris in the the spring').group()605'the the'606```607608Обратные ссылки такого рода редко бывают полезны при простом поиске в строке – существует мало текстовых форматов, которые повторяют данные таким образом, – но вскоре обнаруживается, что они *очень* полезны при выполнении строковых подстановок.609610### Незахватывающие и именованные группы611612Сложные регулярные выражения могут использовать много групп как для захвата интересующих подстрок, так и для группировки и структурирования самого выражения. В сложных RE становится трудно отслеживать номера групп. Существуют две возможности, которые помогают решить эту проблему. Обе используют общий синтаксис расширений регулярных выражений, поэтому сначала рассмотрим его.613614Perl 5 добавил несколько дополнительных возможностей к стандартным регулярным выражениям, и модуль Python [`re`](https://python-all.ru/3.0/library/re.html#module-re) поддерживает большинство из них. Было бы сложно выбрать новые одноклавишные метасимволы или новые специальные последовательности, начинающиеся с `\`, для представления новых возможностей, не делая регулярные выражения Perl запутанно отличными от стандартных. Если бы вы выбрали `&` в качестве нового метасимвола, старые выражения предполагали бы, что `&` – это обычный символ, и не экранировали бы его, записывая `\&` или `[&]`.615616Разработчики Perl выбрали `(?...)` в качестве синтаксиса расширений. `?` сразу после открывающей скобки было синтаксической ошибкой, потому что `?` не к чему было бы применять повтор, так что никаких проблем совместимости это не вызвало. Символы сразу после `?` указывают, какое расширение используется: например, `(?=foo)` – одно (положительная опережающая проверка), а `(?:foo)` – другое (незахватывающая группа с подвыражением `foo`).617618Python добавляет синтаксис расширения к синтаксису расширения Perl. Если первый символ после вопросительного знака – `P`, значит, это расширение, специфичное для Python. В настоящее время существует два таких расширения: `(?P<name>...)` определяет именованную группу, а `(?P=name)` – обратная ссылка на именованную группу. Если будущие версии Perl 5 добавят подобные возможности с другим синтаксисом, модуль [`re`](https://python-all.ru/3.0/library/re.html#module-re) будет изменён для поддержки нового синтаксиса, сохраняя при этом синтаксис, специфичный для Python, ради совместимости.619620Теперь, когда мы рассмотрели общий синтаксис расширений, можно вернуться к возможностям, упрощающим работу с группами в сложных регулярных выражениях. Поскольку группы нумеруются слева направо, а сложное выражение может содержать много групп, отслеживать правильную нумерацию становится трудно. Модифицировать такое сложное регулярное выражение тоже утомительно: добавьте новую группу в начале, и номера всех последующих групп изменятся.621622Иногда нужно использовать группу, чтобы собрать часть регулярного выражения, но нет необходимости извлекать содержимое группы. Можно явно указать это, используя незахватывающую группу: `(?:...)`, где `...` можно заменить любым другим регулярным выражением.623624```python625>>> m = re.match("([abc])+", "abc")626>>> m.groups()627('c',)628>>> m = re.match("(?:[abc])+", "abc")629>>> m.groups()630()631```632633За исключением того, что нельзя извлечь содержимое того, что сопоставила группа, незахватывающая группа ведёт себя точно так же, как захватывающая: в неё можно поместить что угодно, повторить её с помощью метасимвола повторения, такого как `*`, и вложить в другие группы (захватывающие или незахватывающие). `(?:...)` особенно полезен при изменении существующего шаблона, поскольку можно добавлять новые группы, не меняя нумерацию всех остальных групп. Стоит упомянуть, что нет разницы в производительности поиска между захватывающими и незахватывающими группами; ни одна из форм не быстрее другой.634635Более значимая возможность – именованные группы: вместо обращения по номерам группы можно ссылаться по имени.636637Синтаксис именованной группы – одно из расширений, специфичных для Python: `(?P<name>...)`. *name*, очевидно, имя группы. Именованные группы ведут себя точно так же, как захватывающие группы, и дополнительно связывают имя с группой. Методы `MatchObject`, работающие с захватывающими группами, принимают либо целые числа, ссылающиеся на группу по номеру, либо строки, содержащие имя нужной группы. Именованные группы по-прежнему нумеруются, поэтому информацию о группе можно получить двумя способами:638639```python640>>> p = re.compile(r'(?P<word>\b\w+\b)')641>>> m = p.search( '(((( Lots of punctuation )))' )642>>> m.group('word')643'Lots'644>>> m.group(1)645'Lots'646```647648Именованные группы удобны, потому что позволяют использовать легко запоминающиеся имена вместо запоминания номеров. Вот пример регулярного выражения из модуля [`imaplib`](https://python-all.ru/3.0/library/imaplib.html#module-imaplib):649650```python651InternalDate = re.compile(r'INTERNALDATE "'652        r'(?P<day>[ 123][0-9])-(?P<mon>[A-Z][a-z][a-z])-'653        r'(?P<year>[0-9][0-9][0-9][0-9])'654        r' (?P<hour>[0-9][0-9]):(?P<min>[0-9][0-9]):(?P<sec>[0-9][0-9])'655        r' (?P<zonen>[-+])(?P<zoneh>[0-9][0-9])(?P<zonem>[0-9][0-9])'656        r'"')657```658659Очевидно, что получить `m.group('zonem')` гораздо проще, чем запоминать, что нужно получить группу 9.660661Синтаксис обратных ссылок в выражении, например `(...)\1`, относится к номеру группы. Существует вариант, использующий имя группы вместо номера. Это ещё одно расширение Python: `(?P=name)` означает, что содержимое группы с именем *name* должно снова совпадать в текущей позиции. Регулярное выражение для поиска повторяющихся слов, `(\b\w+)\s+\1`, можно также записать как `(?P<word>\b\w+)\s+(?P=word)`:662663```python664>>> p = re.compile(r'(?P<word>\b\w+)\s+(?P=word)')665>>> p.search('Paris in the the spring').group()666'the the'667```668669### Опережающие проверки670671Ещё одна проверка нулевой ширины – опережающая проверка. Опережающие проверки бывают положительными и отрицательными и выглядят так:672673**`(?=...)`**674675Положительная опережающая проверка. Она успешна, если вложенное регулярное выражение, представленное здесь как676677`...`678679, успешно совпадает в текущей позиции, и неуспешна в противном случае. Но как только вложенное выражение проверено, движок сопоставления не продвигается; остальная часть шаблона проверяется прямо с того места, где началась проверка.680681**`(?!...)`**682683Отрицательная опережающая проверка. Это противоположность положительной проверке; она успешна, если вложенное выражение684685*не*686687совпадает в текущей позиции строки.688689Чтобы стало понятнее, рассмотрим случай, где полезна опережающая проверка. Возьмём простой шаблон для сопоставления имени файла и разделения его на имя базы и расширение, разделённые точкой `.`. Например, в `news.rc` `news` – имя базы, а `rc` – расширение.690691Шаблон для сопоставления этого довольно прост:692693`.*[.].*$`694695Обратите внимание, что `.` нужно обрабатывать особым образом, потому что это метасимвол; я поместил его внутрь символьного класса. Также обратите внимание на завершающий `$`; он добавлен, чтобы гарантировать, что вся остальная часть строки должна быть включена в расширение. Это регулярное выражение совпадает с `foo.bar`, `autoexec.bat`, `sendmail.cf` и `printers.conf`.696697Теперь усложним задачу: нужно найти имена файлов, у которых расширение не равно `bat`. Несколько неправильных попыток:698699`.*[.][^b].*$` Первая попытка пытается исключить `bat`, требуя, чтобы первый символ расширения не был `b`. Это неправильно, потому что шаблон также не находит `foo.bar`.700701`.*[.]([^b]..|.[^a].|..[^t])$`702703Выражение становится всё запутаннее, когда мы пытаемся исправить первое решение, требуя, чтобы выполнялся один из следующих случаев: первый символ расширения не равен `b`; второй символ не равен `a`; или третий символ не равен `t`. Такой подход принимает `foo.bar` и отвергает `autoexec.bat`, но требует трёхбуквенного расширения и не будет принимать имена с двухбуквенным расширением, например `sendmail.cf`. Мы снова усложним шаблон, пытаясь это исправить.704705`.*[.]([^b].?.?|.[^a]?.?|..?[^t]?)$`706707В третьей попытке вторая и третья буквы сделаны необязательными, чтобы допустить расширения короче трёх символов, например `sendmail.cf`.708709Шаблон становится очень сложным, что затрудняет его чтение и понимание. Хуже того, если задача изменится и нужно будет исключить как `bat`, так и `exe` в качестве расширений, шаблон станет ещё более запутанным.710711Отрицательная опережающая проверка разрешает всю эту путаницу:712713`.*[.](?!bat$).*$` Отрицательная опережающая проверка означает: если выражение `bat` не совпадает в этой точке, продолжать проверку по остальной части шаблона; если `bat$` совпадает, весь шаблон не сработает. Завершающая `$` необходима, чтобы гарантировать, что такие имена, как `sample.batch`, где расширение только начинается с `bat`, будут допущены.714715Исключить другое расширение теперь просто: достаточно добавить его как альтернативу внутри проверки. Следующий шаблон исключает имена файлов, оканчивающиеся на `bat` или `exe`:716717`.*[.](?!bat$|exe$).*$`718719## Изменение строк720721До этого момента мы выполняли поиск только по статической строке. Регулярные выражения также часто используются для изменения строк различными способами с помощью следующих методов `RegexObject`:722723| Метод/Атрибут | Назначение |724| --- | --- |725| `split()` | Разделяет строку на список, разбивая её везде, где совпадает RE |726| `sub()` | Находит все подстроки, совпадающие с RE, и заменяет их на другую строку |727| `subn()` | Делает то же самое, что и `sub()`, но возвращает новую строку и количество замен |728729### Разделение строк730731Метод `split()` объекта `RegexObject` разбивает строку везде, где совпадает RE, возвращая список фрагментов. Он похож на строковый метод `split()`, но предоставляет гораздо больше гибкости в выборе разделителей; `split()` поддерживает разбиение только по пробельным символам или по фиксированной строке. Как и следовало ожидать, существует также функция уровня модуля [`re.split()`](https://python-all.ru/3.0/library/re.html#re.split).732733#### `.split(string[, maxsplit=0])`734735Разделяет736737*строку*738739по совпадениям регулярного выражения. Если в RE используются захватывающие круглые скобки, то их содержимое также будет возвращено как часть результирующего списка. Если740741*maxsplit*742743не равно нулю, выполняется не более744745*maxsplit*746747разбиений.748749Количество выполняемых разбиений можно ограничить, передав значение для *maxsplit*. Если *maxsplit* не равно нулю, будет выполнено не более *maxsplit* разбиений, а остаток строки возвращается как последний элемент списка. В следующем примере разделителем является любая последовательность неалфавитно-цифровых символов.750751```python752>>> p = re.compile(r'\W+')753>>> p.split('This is a test, short and sweet, of split().')754['This', 'is', 'a', 'test', 'short', 'and', 'sweet', 'of', 'split', '']755>>> p.split('This is a test, short and sweet, of split().', 3)756['This', 'is', 'a', 'test, short and sweet, of split().']757```758759Иногда нужно знать не только текст между разделителями, но и сам разделитель. Если в RE используются захватывающие круглые скобки, то их значения также возвращаются как часть списка. Сравните следующие вызовы:760761```python762>>> p = re.compile(r'\W+')763>>> p2 = re.compile(r'(\W+)')764>>> p.split('This... is a test.')765['This', 'is', 'a', 'test', '']766>>> p2.split('This... is a test.')767['This', '... ', 'is', ' ', 'a', ' ', 'test', '.', '']768```769770Функция уровня модуля [`re.split()`](https://python-all.ru/3.0/library/re.html#re.split) добавляет регулярное выражение в качестве первого аргумента, но в остальном делает то же самое.771772```python773>>> re.split('[\W]+', 'Words, words, words.')774['Words', 'words', 'words', '']775>>> re.split('([\W]+)', 'Words, words, words.')776['Words', ', ', 'words', ', ', 'words', '.', '']777>>> re.split('[\W]+', 'Words, words, words.', 1)778['Words', 'words, words.']779```780781### Поиск и замена782783Еще одна распространенная задача – найти все совпадения с шаблоном и заменить их другой строкой. Метод `sub()` принимает значение замены, которое может быть строкой или функцией, и обрабатываемую строку.784785#### `.sub(replacement, string[, count=0])`786787Возвращает строку, полученную заменой самых левых непересекающихся совпадений RE в *строке* на замену *replacement*. Если шаблон не найден, *строка* возвращается без изменений.788789Необязательный аргумент *count* – максимальное количество заменяемых вхождений шаблона; *count* должно быть неотрицательным целым числом. Значение по умолчанию 0 означает замену всех вхождений.790791Вот простой пример использования метода `sub()`. Он заменяет названия цветов на слово `colour`:792793```python794>>> p = re.compile( '(blue|white|red)')795>>> p.sub( 'colour', 'blue socks and red shoes')796'colour socks and colour shoes'797>>> p.sub( 'colour', 'blue socks and red shoes', count=1)798'colour socks and red shoes'799```800801Метод `subn()` делает то же самое, но возвращает кортеж из двух элементов, содержащий новую строку и количество выполненных замен:802803```python804>>> p = re.compile( '(blue|white|red)')805>>> p.subn( 'colour', 'blue socks and red shoes')806('colour socks and colour shoes', 2)807>>> p.subn( 'colour', 'no colours at all')808('no colours at all', 0)809```810811Пустые совпадения заменяются только тогда, когда они не примыкают к предыдущему совпадению.812813```python814>>> p = re.compile('x*')815>>> p.sub('-', 'abxd')816'-a-b-d-'817```818819Если *replacement* – строка, все управляющие последовательности с обратной косой чертой в ней обрабатываются. То есть `\n` преобразуется в символ новой строки, `\r` – в символ возврата каретки и так далее. Неизвестные управляющие последовательности, такие как `\j`, остаются без изменений. Обратные ссылки, например `\6`, заменяются подстрокой, соответствующей группе в регулярном выражении. Это позволяет включать фрагменты исходного текста в результирующую строку замены.820821Этот пример находит слово `section`, за которым следует строка, заключенная в `{`, `}`, и заменяет `section` на `subsection`:822823```python824>>> p = re.compile('section{ ( [^}]* ) }', re.VERBOSE)825>>> p.sub(r'subsection{\1}','section{First} section{second}')826'subsection{First} subsection{second}'827```828829Существует также синтаксис для ссылки на именованные группы, определяемый синтаксисом `(?P<name>...)`. `\g<name>` использует подстроку, совпавшую с группой с именем `name`, а `\g<number>` – соответствующий номер группы. Таким образом, `\g<2>` эквивалентно `\2`, но не вызывает неоднозначности в строке замены, такой как `\g<2>0`. (`\20` было бы интерпретировано как ссылка на группу 20, а не как ссылка на группу 2, за которой следует литеральный символ `'0'`.) Следующие подстановки эквивалентны, но используют все три варианта строки замены.830831```python832>>> p = re.compile('section{ (?P<name> [^}]* ) }', re.VERBOSE)833>>> p.sub(r'subsection{\1}','section{First}')834'subsection{First}'835>>> p.sub(r'subsection{\g<1>}','section{First}')836'subsection{First}'837>>> p.sub(r'subsection{\g<name>}','section{First}')838'subsection{First}'839```840841*replacement* также может быть функцией, что даёт ещё больше контроля. Если *replacement* – функция, то она вызывается для каждого неперекрывающегося вхождения *pattern*. При каждом вызове функция получает аргумент `MatchObject` для данного совпадения и может использовать эту информацию для вычисления нужной строки замены и возврата её.842843В следующем примере функция замены переводит десятичные числа в шестнадцатеричные:844845```python846>>> def hexrepl( match ):847...     "Return the hex string for a decimal number"848...     value = int( match.group() )849...     return hex(value)850...851>>> p = re.compile(r'\d+')852>>> p.sub(hexrepl, 'Call 65490 for printing, 49152 for user code.')853'Call 0xffd2 for printing, 0xc000 for user code.'854```855856При использовании функции уровня модуля [`re.sub()`](https://python-all.ru/3.0/library/re.html#re.sub) шаблон передаётся первым аргументом. Шаблон может быть строкой или `RegexObject`; если нужно указать флаги регулярных выражений, необходимо либо использовать `RegexObject` в качестве первого параметра, либо применить встроенные модификаторы в шаблоне, например `sub("(?i)b+", "x", "bbbb BBBB")` возвращает `'x x'`.857858## Распространенные проблемы859860Регулярные выражения – мощный инструмент для некоторых приложений, но в некоторых аспектах их поведение не интуитивно и иногда они ведут себя не так, как можно было бы ожидать. В этом разделе будут указаны некоторые наиболее распространённые ошибки.861862### Применение строковых методов863864Иногда использование модуля [`re`](https://python-all.ru/3.0/library/re.html#module-re) является ошибкой. Если вы сопоставляете фиксированную строку или одиночный класс символов и не используете никаких возможностей [`re`](https://python-all.ru/3.0/library/re.html#module-re), таких как флаг `IGNORECASE`, то полная мощь регулярных выражений может не понадобиться. У строк есть несколько методов для выполнения операций с фиксированными строками, и они обычно гораздо быстрее, поскольку реализация представляет собой один небольшой цикл на C, оптимизированный для этой цели, вместо большого, более универсального движка регулярных выражений.865866Один из примеров – замена одной фиксированной строки на другую; например, можно заменить `word` на `deed`. `re.sub()` кажется подходящей функцией для этого, но рассмотрите метод `replace()`. Обратите внимание, что `replace()` также заменит `word` внутри слов, превратив `swordfish` в `sdeedfish`, но наивное RE `word` сделало бы то же самое. (Чтобы избежать замены в частях слов, шаблон должен быть `\bword\b`, чтобы требовалось наличие границы слова с обеих сторон от `word`. Это выходит за рамки возможностей `replace()`.)867868Еще одна распространенная задача – удаление всех вхождений одного символа из строки или замена его другим символом. Это можно сделать с помощью чего-то вроде `re.sub('\n', ' ', S)`, но `translate()` способен выполнить обе задачи и будет быстрее любой операции с регулярными выражениями.869870Короче говоря, прежде чем обращаться к модулю [`re`](https://python-all.ru/3.0/library/re.html#module-re), подумайте, можно ли решить вашу задачу с помощью более быстрого и простого строкового метода.871872### match() и search()873874Функция `match()` проверяет только, совпадает ли регулярное выражение в начале строки, тогда как `search()` будет сканировать строку вперед в поисках совпадения. Важно помнить об этом различии. Помните, `match()` сообщит об успешном совпадении только в том случае, если оно начинается с позиции 0; если совпадение не начинается с нуля, `match()` *не* сообщит о нем.875876```python877>>> print(re.match('super', 'superstition').span())878(0, 5)879>>> print(re.match('super', 'insuperable'))880None881```882883С другой стороны, `search()` будет сканировать строку вперед, сообщая о первом найденном совпадении.884885```python886>>> print(re.search('super', 'superstition').span())887(0, 5)888>>> print(re.search('super', 'insuperable').span())889(2, 7)890```891892Иногда возникает соблазн продолжать использовать [`re.match()`](https://python-all.ru/3.0/library/re.html#re.match) и просто добавить `.*` в начало регулярного выражения. Сопротивляйтесь этому искушению и используйте [`re.search()`](https://python-all.ru/3.0/library/re.html#re.search). Компилятор регулярных выражений выполняет некоторый анализ, чтобы ускорить поиск совпадения. Один из таких анализов определяет, каким должен быть первый символ совпадения; например, шаблон, начинающийся с `Crow`, должен начинаться с `'C'`. Этот анализ позволяет движку быстро просматривать строку в поисках начального символа, пробуя полное совпадение только в том случае, если найден `'C'`.893894Добавление `.*` сводит на нет эту оптимизацию, требуя сканирования до конца строки и последующего возврата для поиска совпадения с остальной частью регулярного выражения. Вместо этого используйте [`re.search()`](https://python-all.ru/3.0/library/re.html#re.search).895896### Жадный и нежадный режимы897898При повторении регулярного выражения, как в `a*`, результирующее действие – поглотить как можно больше шаблона. Этот факт часто подводит при попытке сопоставить пару сбалансированных разделителей, например угловые скобки, окружающие HTML-тег. Наивный шаблон для сопоставления одного HTML-тега не работает из-за жадной природы `.*`.899900```python901>>> s = '<html><head><title>Title</title>'902>>> len(s)90332904>>> print(re.match('<.*>', s).span())905(0, 32)906>>> print(re.match('<.*>', s).group())907<html><head><title>Title</title>908```909910Регулярное выражение находит `'<'` в `<html>`, и `.*` поглощает остальную часть строки. Однако в регулярном выражении еще осталась часть, и `>` не может совпасть в конце строки, поэтому движку регулярных выражений приходится возвращаться посимвольно, пока он не найдет совпадение для `>`. Итоговое совпадение простирается от `'<'` в `<html>` до `'>'` в `</title>`, что не является желаемым результатом.911912В этом случае решение – использовать нежадные квантификаторы `*?`, `+?`, `??` или `{m,n}?`, которые сопоставляют как можно *меньше* текста. В приведенном выше примере `'>'` пробуется сразу после того, как первое `'<'` совпадает, и когда это не удается, движок продвигается на один символ за раз, повторяя попытку `'>'` на каждом шаге. Это дает правильный результат:913914```python915>>> print(re.match('<.*?>', s).group())916<html>917```918919(Заметьте, что разбор HTML или XML с помощью регулярных выражений – дело мучительное. Быстрые и грязные шаблоны справятся с обычными случаями, но у HTML и XML есть особые случаи, которые сломают очевидное регулярное выражение; к тому времени, как вы напишете регулярное выражение, обрабатывающее все возможные случаи, шаблоны станут *очень* сложными. Используйте для таких задач модуль разбора HTML или XML.)920921### Без использования re.VERBOSE922923К этому моменту вы, вероятно, заметили, что регулярные выражения – это очень компактная запись, но они не слишком читаемы. RE умеренной сложности могут превратиться в длинные наборы обратных слешей, скобок и метасимволов, что затрудняет их чтение и понимание.924925Для таких регулярных выражений может быть полезно указать флаг `re.VERBOSE` при компиляции, так как он позволяет форматировать регулярное выражение более наглядно.926927Флаг `re.VERBOSE` имеет несколько эффектов. Пробельные символы в регулярном выражении, которые *не находятся* внутри класса символов, игнорируются. Это означает, что выражение, такое как `dog | cat`, эквивалентно менее читаемому `dog|cat`, но `[a b]` все равно будет соответствовать символам `'a'`, `'b'` или пробелу. Кроме того, можно размещать комментарии внутри регулярного выражения; комментарии начинаются с символа `#` и продолжаются до следующего перевода строки. При использовании с тройными кавычками это позволяет форматировать регулярные выражения более аккуратно:928929```python930pat = re.compile(r"""931 \s*                 # Skip leading whitespace932 (?P<header>[^:]+)   # Header name933 \s* :               # Whitespace, and a colon934 (?P<value>.*?)      # The header's value -- *? used to935                     # lose the following trailing whitespace936 \s*$                # Trailing whitespace to end-of-line937""", re.VERBOSE)938```939940Это гораздо читаемее, чем:941942```python943pat = re.compile(r"\s*(?P<header>[^:]+)\s*:(?P<value>.*?)\s*$")944```945946## Обратная связь947948Регулярные выражения – сложная тема. Помог ли вам этот документ понять их? Были ли части, которые остались неясными, или проблемы, с которыми вы столкнулись и которые не были здесь рассмотрены? Если да, отправьте предложения по улучшению автору.949950Самая полная книга по регулярным выражениям – это, почти наверняка, «Mastering Regular Expressions» Джеффри Фридла, издательство O'Reilly. К сожалению, она полностью сосредоточена на реализациях регулярных выражений в Perl и Java и не содержит никакого материала по Python, поэтому она не пригодится в качестве справочника для программирования на Python. (Первое издание охватывало ныне удаленный модуль Python `regex`, что вам не очень поможет.) Подумайте о том, чтобы взять ее в библиотеке.951952Сноски953954| [\[1\]](https://python-all.ru/3.0/howto/regex.html#id1) | Введено в Python 2.2.2. |955| --- | --- |956