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

regex.md

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

1> **Источник:** https://python-all.ru/2.6/howto/regex.html2>3> «Документация Python на русском» – неофициальный перевод официальной документации Python: версии от 2.6 до 3.16, полнотекстовый поиск, английский оригинал рядом с переводом. Эта Markdown-версия страницы предназначена для работы с LLM: вставьте её в ChatGPT, Claude или Cursor.45---67# Регулярные выражения: руководство89| Автор: | A.M. Kuchling \<[amk@amk.ca](https://python-all.ru/2.6/howto/regex.html)\> |10| --- | --- |11| Версия: | 0.05 |1213Аннотация1415Этот документ представляет собой вводное руководство по использованию регулярных выражений в Python с модулем [`re`](https://python-all.ru/2.6/library/re.html#module-re). Он даёт более мягкое введение, чем соответствующий раздел в справочнике библиотеки.1617## Введение1819Модуль [`re`](https://python-all.ru/2.6/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.6/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/2.6/library/re.html#module-re) предоставляет интерфейс к движку регулярных выражений, позволяя компилировать РВ в объекты и затем выполнять с ними сопоставление.144145### Компиляция регулярных выражений146147Регулярные выражения компилируются в объекты шаблонов, которые имеют методы для различных операций, таких как поиск совпадений с шаблоном или выполнение подстановок в строке.148149```python150>>> import re151>>> p = re.compile('ab*')152>>> print p153<_sre.SRE_Pattern object at 80b4150>154```155156[`re.compile()`](https://python-all.ru/2.6/library/re.html#re.compile) также принимает необязательный аргумент *flags*, используемый для включения различных специальных возможностей и синтаксических вариаций. Мы рассмотрим доступные настройки позже, а пока достаточно одного примера:157158```python159>>> p = re.compile('ab*', re.IGNORECASE)160```161162РВ передаётся в [`re.compile()`](https://python-all.ru/2.6/library/re.html#re.compile) в виде строки. РВ обрабатываются как строки, потому что регулярные выражения не являются частью основного языка Python, и для их записи не было создано специального синтаксиса. (Есть приложения, которым РВ вообще не нужны, так что нет необходимости раздувать спецификацию языка, включая их.) Вместо этого модуль [`re`](https://python-all.ru/2.6/library/re.html#module-re) – это просто модуль-расширение на C, входящий в состав Python, как и модули [`socket`](https://python-all.ru/2.6/library/socket.html#module-socket) или [`zlib`](https://python-all.ru/2.6/library/zlib.html#module-zlib).163164Размещение RE в строках сохраняет язык Python более простым, но имеет один недостаток, который является темой следующего раздела.165166### Проблема обратной косой черты167168Как было сказано ранее, регулярные выражения используют символ обратной косой черты (`'\'`) для обозначения специальных форм или для использования специальных символов без их специального значения. Это конфликтует с использованием того же символа в строковых литералах Python для той же цели.169170Допустим, нужно написать РВ, которое совпадает со строкой `\section`, которая может встречаться в файле LaTeX. Чтобы понять, что писать в коде программы, начните с нужной строки для сопоставления. Затем нужно экранировать все обратные слэши и другие метасимволы, предваряя их обратным слэшем, в результате получится строка `\\section`. Результирующая строка, которую нужно передать в [`re.compile()`](https://python-all.ru/2.6/library/re.html#re.compile), должна быть `\\section`. Однако, чтобы выразить это в виде строкового литерала Python, оба обратных слэша необходимо экранировать *снова*.171172| Символы | Этап |173| --- | --- |174| `\section` | Строка для совпадения |175| `\\section` | Экранированный обратный слэш для [`re.compile()`](https://python-all.ru/2.6/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Когда у вас есть объект, представляющий скомпилированное регулярное выражение, что с ним делать? Объекты-шаблоны имеют несколько методов и атрибутов. Здесь будут рассмотрены только самые важные; обратитесь к документации [`re`](https://python-all.ru/2.6/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/2.6/glossary.html#term-iterator). |198199`match()` и `search()` возвращают `None`, если совпадение не найдено. Если совпадение найдено, возвращается экземпляр `MatchObject`, содержащий информацию о совпадении: где оно начинается и заканчивается, какую подстроку захватило и т.д.200201Изучить это можно, интерактивно экспериментируя с модулем [`re`](https://python-all.ru/2.6/library/re.html#module-re). Если у вас установлен Tkinter, можно также посмотреть на `Tools/scripts/redemo.py` – демонстрационную программу, включённую в дистрибутив Python. Она позволяет вводить регулярные выражения и строки, а также показывает, соответствует ли RE строке или нет. `redemo.py` может быть весьма полезен при отладке сложных RE. [Kodos](https://python-all.ru/2.6/howto/regex.html) от Фила Шварца – также интерактивный инструмент для разработки и тестирования RE-шаблонов.202203В этом HOWTO используется стандартный интерпретатор Python для примеров. Сначала запустите интерпретатор Python, импортируйте модуль [`re`](https://python-all.ru/2.6/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>>> print 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()` returns the substring that was matched by the RE. `start()` and `end()` return the starting and ending index of the match. `span()` returns both start and end indexes in a single tuple. Since the `match()` method only checks if the RE matches at the start of a string, `start()` will always be zero. However, the `search()` method of patterns scans through the string, so the match may not start at zero in that case.250251```python252>>> print p.match('::: message')253None254>>> m = p.search('::: message') ; print m255<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Два метода шаблонов возвращают все совпадения для шаблона. `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/2.6/glossary.html#term-iterator). [\[1\]](https://python-all.ru/2.6/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Не обязательно создавать объект шаблона и вызывать его методы; модуль [`re`](https://python-all.ru/2.6/library/re.html#module-re) также предоставляет функции верхнего уровня: `match()`, `search()`, `findall()`, `sub()` и так далее. Эти функции принимают те же аргументы, что и соответствующий метод шаблона, с добавлением строки 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Внутри эти функции просто создают объект шаблона и вызывают на нём соответствующий метод. Они также сохраняют скомпилированный объект в кеше, поэтому последующие вызовы с тем же регулярным выражением выполняются быстрее.307308Стоит ли использовать эти функции уровня модуля, или лучше получить шаблон и вызывать его методы самостоятельно? Этот выбор зависит от того, как часто будет использоваться регулярное выражение, и от личного стиля кодирования. Если регулярное выражение используется только в одном месте кода, то, вероятно, удобнее воспользоваться функциями модуля. Если программа содержит много регулярных выражений или переиспользует одни и те же в нескольких местах, то имеет смысл собрать все определения в одном месте, в разделе кода, который компилирует все регулярные выражения заранее. В качестве примера из стандартной библиотеки, вот выдержка из `xmllib.py`:309310```python311ref = re.compile( ... )312entityref = re.compile( ... )313charref = re.compile( ... )314starttagopen = re.compile( ... )315```316317Я обычно предпочитаю работать со скомпилированным объектом, даже для одноразового использования, но мало кто будет таким же пуристом в этом вопросе, как я.318319### Флаги компиляции320321Флаги компиляции позволяют изменять некоторые аспекты работы регулярных выражений. Флаги доступны в модуле [`re`](https://python-all.ru/2.6/library/re.html#module-re) под двумя именами: длинным, например `IGNORECASE`, и коротким, однобуквенным, например `I`. (Если вы знакомы с модификаторами шаблонов в Perl, то однобуквенные формы используют те же буквы; например, короткая форма [`re.VERBOSE`](https://python-all.ru/2.6/library/re.html#re.VERBOSE) – это [`re.X`](https://python-all.ru/2.6/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) регулярные выражения, которые можно организовать более чисто и понятно. |332| `UNICODE`, `U` | Делает несколько escape-последовательностей, таких как `\w`, `\b`, `\s` и `\d`, зависимыми от базы данных символов Unicode. |333334#### `I`335336#### `IGNORECASE`337338Выполняет сопоставление без учёта регистра; классы символов и строковые литералы будут сопоставляться с буквами, игнорируя регистр. Например,339340`[A-Z]`341342будет также находить строчные буквы, а343344`Spam`345346будет соответствовать347348`Spam`349350,351352`spam`353354или355356`spAM`357358. Это приведение к нижнему регистру не учитывает текущую локаль; оно будет учитывать, если также установить флаг359360`LOCALE`361362.363364#### `L`365366#### `LOCALE`367368Делает `\w`, `\W`, `\b` и `\B` зависимыми от текущей локали.369370Локали – это возможность библиотеки C, предназначенная для написания программ, учитывающих языковые различия. Например, при обработке французского текста может понадобиться написать `\\w+` для поиска слов, но `\\w` соответствует только классу символов `[A-Za-z]`; он не будет находить `'é'` или `'ç'`. Если система настроена правильно и выбрана французская локаль, некоторые функции C сообщат программе, что `'é'` также следует считать буквой. Установка флага `LOCALE` при компиляции регулярного выражения заставит результирующий скомпилированный объект использовать эти функции C для `\\w`; это медленнее, но позволяет `\\w+` находить французские слова так, как и ожидается.371372#### `M`373374#### `MULTILINE`375376(`^` и `$` пока не объяснены; они будут представлены в разделе [*Дополнительные метасимволы*](https://python-all.ru/2.6/howto/regex.html#more-metacharacters).)377378Обычно `^` сопоставляется только в начале строки, а `$` – только в конце строки и непосредственно перед символом новой строки (если он есть) в конце строки. Когда указан этот флаг, `^` сопоставляется в начале строки и в начале каждой строки внутри строки, сразу после каждого символа новой строки. Аналогично, метасимвол `$` сопоставляется в конце строки и в конце каждой строки (непосредственно перед каждым символом новой строки).379380#### `S`381382#### `DOTALL`383384Заставляет специальный символ385386`'.'`387388сопоставляться с любым символом, включая символ новой строки; без этого флага389390`'.'`391392будет сопоставляться с любым символом,393394*кроме*395396символа новой строки.397398#### `U`399400#### `UNICODE`401402Делает403404`\w`405406,407408`\W`409410,411412`\b`413414,415416`\B`417418,419420`\d`421422,423424`\D`425426,427428`\s`429430и431432`\S`433434зависимыми от базы данных свойств символов Unicode.435436#### `X`437438#### `VERBOSE`439440Этот флаг позволяет писать более читаемые регулярные выражения, предоставляя больше гибкости в их форматировании. Когда этот флаг указан, пробельные символы внутри строки регулярного выражения игнорируются, за исключением случаев, когда пробел находится внутри символьного класса или перед ним стоит неэкранированная обратная косая черта; это позволяет более четко организовывать и расставлять отступы в регулярном выражении. Этот флаг также позволяет помещать комментарии внутри регулярного выражения, которые будут игнорироваться движком; комментарии обозначаются символом `'#'`, который не находится внутри символьного класса и не предваряется неэкранированной обратной косой чертой.441442Например, вот RE, использующий [`re.VERBOSE`](https://python-all.ru/2.6/library/re.html#re.VERBOSE); видите, насколько его легче читать?443444```python445charref = re.compile(r"""446 &[#]                # Start of a numeric entity reference447 (448     0[0-7]+         # Octal form449   | [0-9]+          # Decimal form450   | x[0-9a-fA-F]+   # Hexadecimal form451 )452 ;                   # Trailing semicolon453""", re.VERBOSE)454```455456Без режима verbose регулярное выражение выглядело бы так:457458```python459charref = re.compile("&#(0[0-7]+"460                     "|[0-9]+"461                     "|x[0-9a-fA-F]+);")462```463464В приведённом выше примере использовалась автоматическая конкатенация строковых литералов Python для разбиения RE на более мелкие части, но её по-прежнему сложнее понять, чем версию с [`re.VERBOSE`](https://python-all.ru/2.6/library/re.html#re.VERBOSE).465466## Дополнительные возможности шаблонов467468До сих пор мы рассмотрели только часть возможностей регулярных выражений. В этом разделе мы рассмотрим некоторые новые метасимволы и способы использования групп для извлечения частей сопоставленного текста.469470### Дополнительные метасимволы471472Остались некоторые метасимволы, которые мы ещё не рассмотрели. Большинство из них будет рассмотрено в этом разделе.473474Некоторые из оставшихся метасимволов, которые предстоит обсудить, – это *проверки с нулевой шириной* (zero-width assertions). Они не заставляют движок продвигаться по строке; вместо этого они вообще не потребляют символы и просто успешно проходят или нет. Например, `\\b` – это проверка того, что текущая позиция находится на границе слова; позиция `\\b` вообще не меняется. Это означает, что проверки с нулевой шириной никогда не следует повторять, потому что если они один раз совпадают в данной позиции, то, очевидно, могут совпадать бесконечное количество раз.475476**`|`**477478Альтернатива, или оператор «или». Если A и B – регулярные выражения, то `A|B` будет соответствовать любой строке, которая соответствует либо `A`, либо `B`. `|` имеет очень низкий приоритет, чтобы разумно работать при альтернативе многозначных строк. `Crow|Servo` будет соответствовать либо `Crow`, либо `Servo`, а не `Cro`, `'w'` или `'S'`, и `ervo`.479480Чтобы сопоставить литерал `'|'`, используйте `\|` или заключите его в символьный класс, как в `[|]`.481482**`^`**483484Соответствует началу строк. Если не установлен флаг `MULTILINE`, будет соответствовать только началу всей строки. В режиме `MULTILINE` также соответствует сразу после каждого символа новой строки внутри строки.485486Например, если нужно найти слово `From` только в начале строки, следует использовать RE `^From`.487488```python489>>> print re.search('^From', 'From Here to Eternity')490<re.MatchObject instance at 80c1520>491>>> print re.search('^From', 'Reciting From Memory')492None493```494495**`$`**496497Совпадение в конце строки, которая определяется как конец строки или любая позиция, за которой следует символ перевода строки.498499```python500>>> print re.search('}$', '{block}')501<re.MatchObject instance at 80adfa8>502>>> print re.search('}$', '{block} ')503None504>>> print re.search('}$', '{block}\n')505<re.MatchObject instance at 80adfa8>506```507508Чтобы сопоставить литерал `'$'`, используйте `\$` или заключите его в символьный класс, как в `[$]`.509510**`\A`**511512Совпадает только в начале строки. Если не используется режим513514`MULTILINE`515516,517518`\A`519520и521522`^`523524по сути одно и то же. В режиме525526`MULTILINE`527528они различаются:529530`\A`531532по-прежнему совпадает только в начале строки, а533534`^`535536может совпадать в любом месте строки после символа новой строки.537538**`\Z`**539540Совпадение только в конце строки.541542**`\\b`**543544Граница слова. Это утверждение нулевой длины, которое совпадает только в начале или конце слова. Слово определяется как последовательность буквенно-цифровых символов, поэтому конец слова обозначается пробелом или не-буквенно-цифровым символом.545546Следующий пример находит `class` только когда это целое слово; не будет находить, когда оно является частью другого слова.547548```python549>>> p = re.compile(r'\bclass\b')550>>> print p.search('no class at all')551<re.MatchObject instance at 80c8f28>552>>> print p.search('the declassified algorithm')553None554>>> print p.search('one subclass is')555None556```557558При использовании этой специальной последовательности нужно помнить о двух тонкостях. Первая: это самое серьёзное столкновение между строковыми литералами Python и последовательностями регулярных выражений. В строковых литералах Python `\b` – это символ backspace (ASCII 8). Если не используются сырые строки, Python преобразует `\b` в backspace, и регулярное выражение будет работать не так, как ожидается. Следующий пример выглядит так же, как предыдущее регулярное выражение, но перед строкой RE нет `'r'`.559560```python561>>> p = re.compile('\bclass\b')562>>> print p.search('no class at all')563None564>>> print p.search('\b' + 'class' + '\b')565<re.MatchObject instance at 80c3ee0>566```567568Внутри символьного класса, где эта проверка не нужна, `\b` представляет символ backspace (для совместимости со строковыми литералами Python).569570**`\B`**571572Ещё одна проверка с нулевой длиной; противоположность573574`\b`575576. Совпадает, когда текущая позиция не находится на границе слова.577578### Группировка579580Часто требуется получить больше информации, чем просто – совпало RE или нет. Регулярные выражения часто используются для разбора строк с помощью RE, разделённого на несколько подгрупп, каждая из которых соответствует интересующему компоненту. Например, строка заголовка RFC-822 разделена на имя заголовка и значение, разделённые `':'`, так:581582```python583From: author@example.com584User-Agent: Thunderbird 1.5.0.9 (X11/20061227)585MIME-Version: 1.0586To: editor@example.com587```588589Это можно обработать, написав регулярное выражение, которое сопоставляет всю строку заголовка, и имеет одну группу для имени заголовка и другую – для значения заголовка.590591Группы выделяются метасимволами `'('` и `')'`. `'('` и `')'` имеют примерно тот же смысл, что и в математических выражениях: они группируют содержащиеся в них выражения, и содержимое группы можно повторять с помощью квантификаторов, таких как `*`, `+`, `?` или `{m,n}`. Например, `(ab)*` найдёт ноль или более повторений `ab`.592593```python594>>> p = re.compile('(ab)*')595>>> print p.match('ababababab').span()596(0, 10)597```598599Группы, обозначенные с помощью `'('`, `')'`, также захватывают начальный и конечный индекс совпавшего текста; их можно получить, передав аргумент `group()`, `start()`, `end()` и `span()`. Группы нумеруются начиная с 0. Группа 0 всегда присутствует; это всё RE, поэтому методы `MatchObject` по умолчанию принимают группу 0 в качестве аргумента. Позже мы увидим, как задавать группы, которые не захватывают диапазон совпавшего текста.600601```python602>>> p = re.compile('(a)b')603>>> m = p.match('ab')604>>> m.group()605'ab'606>>> m.group(0)607'ab'608```609610Подгруппы нумеруются слева направо, начиная с 1. Группы могут быть вложенными; чтобы определить номер, нужно просто посчитать открывающие скобки, идя слева направо.611612```python613>>> p = re.compile('(a(b)c)d')614>>> m = p.match('abcd')615>>> m.group(0)616'abcd'617>>> m.group(1)618'abc'619>>> m.group(2)620'b'621```622623`group()` можно передать несколько номеров групп одновременно; в этом случае он вернёт кортеж с соответствующими значениями для этих групп.624625```python626>>> m.group(2,1,2)627('b', 'abc', 'b')628```629630Метод `groups()` возвращает кортеж, содержащий строки для всех подгрупп – от 1 до их общего количества.631632```python633>>> m.groups()634('abc', 'b')635```636637Обратные ссылки в шаблоне позволяют указать, что содержимое ранее захваченной группы должно также присутствовать в текущей позиции строки. Например, `\1` будет успешным, если точное содержимое группы 1 можно найти в текущей позиции, иначе – нет. Помните, что строковые литералы Python также используют обратную косую черту с числами для включения произвольных символов в строку, поэтому обязательно используйте сырую строку при включении обратных ссылок в RE.638639Например, следующее RE обнаруживает удвоенные слова в строке.640641```python642>>> p = re.compile(r'(\b\w+)\s+\1')643>>> p.search('Paris in the the spring').group()644'the the'645```646647Обратные ссылки такого рода редко бывают полезны при простом поиске в строке – существует мало текстовых форматов, которые повторяют данные таким образом, – но вскоре обнаруживается, что они *очень* полезны при выполнении строковых подстановок.648649### Незахватывающие и именованные группы650651Сложные регулярные выражения могут использовать много групп как для захвата интересующих подстрок, так и для группировки и структурирования самого выражения. В сложных RE становится трудно отслеживать номера групп. Существуют две возможности, которые помогают решить эту проблему. Обе используют общий синтаксис расширений регулярных выражений, поэтому сначала рассмотрим его.652653Perl 5 добавил несколько дополнительных возможностей к стандартным регулярным выражениям, и модуль Python [`re`](https://python-all.ru/2.6/library/re.html#module-re) поддерживает большинство из них. Было бы сложно выбрать новые одноклавишные метасимволы или новые специальные последовательности, начинающиеся с `\`, для представления новых возможностей, не делая регулярные выражения Perl запутанно отличными от стандартных. Если бы вы выбрали `&` в качестве нового метасимвола, старые выражения предполагали бы, что `&` – это обычный символ, и не экранировали бы его, записывая `\&` или `[&]`.654655Разработчики Perl выбрали `(?...)` в качестве синтаксиса расширений. `?` сразу после открывающей скобки было синтаксической ошибкой, потому что `?` не к чему было бы применять повтор, так что никаких проблем совместимости это не вызвало. Символы сразу после `?` указывают, какое расширение используется: например, `(?=foo)` – одно (положительная опережающая проверка), а `(?:foo)` – другое (незахватывающая группа с подвыражением `foo`).656657Python добавляет синтаксис расширения к синтаксису расширения Perl. Если первый символ после вопросительного знака – `P`, значит, это расширение, специфичное для Python. В настоящее время существует два таких расширения: `(?P<name>...)` определяет именованную группу, а `(?P=name)` – обратная ссылка на именованную группу. Если будущие версии Perl 5 добавят подобные возможности с другим синтаксисом, модуль [`re`](https://python-all.ru/2.6/library/re.html#module-re) будет изменён для поддержки нового синтаксиса, сохраняя при этом синтаксис, специфичный для Python, ради совместимости.658659Теперь, когда мы рассмотрели общий синтаксис расширений, можно вернуться к возможностям, упрощающим работу с группами в сложных регулярных выражениях. Поскольку группы нумеруются слева направо, а сложное выражение может содержать много групп, отслеживать правильную нумерацию становится трудно. Модифицировать такое сложное регулярное выражение тоже утомительно: добавьте новую группу в начале, и номера всех последующих групп изменятся.660661Иногда нужно использовать группу, чтобы собрать часть регулярного выражения, но нет необходимости извлекать содержимое группы. Можно явно указать это, используя незахватывающую группу: `(?:...)`, где `...` можно заменить любым другим регулярным выражением.662663```python664>>> m = re.match("([abc])+", "abc")665>>> m.groups()666('c',)667>>> m = re.match("(?:[abc])+", "abc")668>>> m.groups()669()670```671672За исключением того, что нельзя извлечь содержимое того, что сопоставила группа, незахватывающая группа ведёт себя точно так же, как захватывающая: в неё можно поместить что угодно, повторить её с помощью метасимвола повторения, такого как `*`, и вложить в другие группы (захватывающие или незахватывающие). `(?:...)` особенно полезен при изменении существующего шаблона, поскольку можно добавлять новые группы, не меняя нумерацию всех остальных групп. Стоит упомянуть, что нет разницы в производительности поиска между захватывающими и незахватывающими группами; ни одна из форм не быстрее другой.673674Более значимая возможность – именованные группы: вместо обращения по номерам группы можно ссылаться по имени.675676Синтаксис именованной группы – одно из расширений, специфичных для Python: `(?P<name>...)`. *name*, очевидно, имя группы. Именованные группы ведут себя точно так же, как захватывающие группы, и дополнительно связывают имя с группой. Методы `MatchObject`, работающие с захватывающими группами, принимают либо целые числа, ссылающиеся на группу по номеру, либо строки, содержащие имя нужной группы. Именованные группы по-прежнему нумеруются, поэтому информацию о группе можно получить двумя способами:677678```python679>>> p = re.compile(r'(?P<word>\b\w+\b)')680>>> m = p.search( '(((( Lots of punctuation )))' )681>>> m.group('word')682'Lots'683>>> m.group(1)684'Lots'685```686687Именованные группы удобны, потому что позволяют использовать легко запоминающиеся имена вместо запоминания номеров. Вот пример регулярного выражения из модуля [`imaplib`](https://python-all.ru/2.6/library/imaplib.html#module-imaplib):688689```python690InternalDate = re.compile(r'INTERNALDATE "'691        r'(?P<day>[ 123][0-9])-(?P<mon>[A-Z][a-z][a-z])-'692        r'(?P<year>[0-9][0-9][0-9][0-9])'693        r' (?P<hour>[0-9][0-9]):(?P<min>[0-9][0-9]):(?P<sec>[0-9][0-9])'694        r' (?P<zonen>[-+])(?P<zoneh>[0-9][0-9])(?P<zonem>[0-9][0-9])'695        r'"')696```697698Очевидно, что получить `m.group('zonem')` гораздо проще, чем запоминать, что нужно получить группу 9.699700Синтаксис обратных ссылок в выражении, например `(...)\1`, относится к номеру группы. Существует вариант, использующий имя группы вместо номера. Это ещё одно расширение Python: `(?P=name)` означает, что содержимое группы с именем *name* должно снова совпадать в текущей позиции. Регулярное выражение для поиска повторяющихся слов, `(\b\w+)\s+\1`, можно также записать как `(?P<word>\b\w+)\s+(?P=word)`:701702```python703>>> p = re.compile(r'(?P<word>\b\w+)\s+(?P=word)')704>>> p.search('Paris in the the spring').group()705'the the'706```707708### Опережающие проверки709710Ещё одна проверка нулевой ширины – опережающая проверка. Опережающие проверки бывают положительными и отрицательными и выглядят так:711712**`(?=...)`**713714Положительная опережающая проверка. Она успешна, если вложенное регулярное выражение, представленное здесь как715716`...`717718, успешно совпадает в текущей позиции, и неуспешна в противном случае. Но как только вложенное выражение проверено, движок сопоставления не продвигается; остальная часть шаблона проверяется прямо с того места, где началась проверка.719720**`(?!...)`**721722Отрицательная опережающая проверка. Это противоположность положительной проверке; она успешна, если вложенное выражение723724*не*725726совпадает в текущей позиции строки.727728Чтобы стало понятнее, рассмотрим случай, где полезна опережающая проверка. Возьмём простой шаблон для сопоставления имени файла и разделения его на имя базы и расширение, разделённые точкой `.`. Например, в `news.rc` `news` – имя базы, а `rc` – расширение.729730Шаблон для сопоставления этого довольно прост:731732`.*[.].*$`733734Обратите внимание, что `.` нужно обрабатывать особым образом, потому что это метасимвол; я поместил его внутрь символьного класса. Также обратите внимание на завершающий `$`; он добавлен, чтобы гарантировать, что вся остальная часть строки должна быть включена в расширение. Это регулярное выражение совпадает с `foo.bar`, `autoexec.bat`, `sendmail.cf` и `printers.conf`.735736Теперь усложним задачу: нужно найти имена файлов, у которых расширение не равно `bat`. Несколько неправильных попыток:737738`.*[.][^b].*$` Первая попытка пытается исключить `bat`, требуя, чтобы первый символ расширения не был `b`. Это неправильно, потому что шаблон также не находит `foo.bar`.739740`.*[.]([^b]..|.[^a].|..[^t])$`741742Выражение становится всё запутаннее, когда мы пытаемся исправить первое решение, требуя, чтобы выполнялся один из следующих случаев: первый символ расширения не равен `b`; второй символ не равен `a`; или третий символ не равен `t`. Такой подход принимает `foo.bar` и отвергает `autoexec.bat`, но требует трёхбуквенного расширения и не будет принимать имена с двухбуквенным расширением, например `sendmail.cf`. Мы снова усложним шаблон, пытаясь это исправить.743744`.*[.]([^b].?.?|.[^a]?.?|..?[^t]?)$`745746В третьей попытке вторая и третья буквы сделаны необязательными, чтобы допустить расширения короче трёх символов, например `sendmail.cf`.747748Шаблон становится очень сложным, что затрудняет его чтение и понимание. Хуже того, если задача изменится и нужно будет исключить как `bat`, так и `exe` в качестве расширений, шаблон станет ещё более запутанным.749750Отрицательная опережающая проверка разрешает всю эту путаницу:751752`.*[.](?!bat$).*$` Отрицательная опережающая проверка означает: если выражение `bat` не совпадает в этой точке, продолжать проверку по остальной части шаблона; если `bat$` совпадает, весь шаблон не сработает. Завершающая `$` необходима, чтобы гарантировать, что такие имена, как `sample.batch`, где расширение только начинается с `bat`, будут допущены.753754Исключить другое расширение теперь просто: достаточно добавить его как альтернативу внутри проверки. Следующий шаблон исключает имена файлов, оканчивающиеся на `bat` или `exe`:755756`.*[.](?!bat$|exe$).*$`757758## Изменение строк759760До этого момента мы просто выполняли поиск в статической строке. Регулярные выражения также часто используются для изменения строк различными способами с помощью следующих методов шаблона:761762| Метод/Атрибут | Назначение |763| --- | --- |764| `split()` | Разделяет строку на список, разбивая её везде, где совпадает RE |765| `sub()` | Находит все подстроки, совпадающие с RE, и заменяет их на другую строку |766| `subn()` | Делает то же самое, что и `sub()`, но возвращает новую строку и количество замен |767768### Разделение строк769770Метод `split()` шаблона разбивает строку на части везде, где совпадение с регулярным выражением, и возвращает список фрагментов. Он похож на `split()` для строк, но предоставляет гораздо больше возможностей для задания разделителей; `split()` поддерживает только разбиение по пробельным символам или по фиксированной строке. Как и следовало ожидать, существует также функция уровня модуля [`re.split()`](https://python-all.ru/2.6/library/re.html#re.split).771772#### `.split(string[, maxsplit=0])`773774Разделяет775776*строку*777778по совпадениям регулярного выражения. Если в RE используются захватывающие круглые скобки, то их содержимое также будет возвращено как часть результирующего списка. Если779780*maxsplit*781782не равно нулю, выполняется не более783784*maxsplit*785786разбиений.787788Количество выполняемых разбиений можно ограничить, передав значение для *maxsplit*. Если *maxsplit* не равно нулю, будет выполнено не более *maxsplit* разбиений, а остаток строки возвращается как последний элемент списка. В следующем примере разделителем является любая последовательность неалфавитно-цифровых символов.789790```python791>>> p = re.compile(r'\W+')792>>> p.split('This is a test, short and sweet, of split().')793['This', 'is', 'a', 'test', 'short', 'and', 'sweet', 'of', 'split', '']794>>> p.split('This is a test, short and sweet, of split().', 3)795['This', 'is', 'a', 'test, short and sweet, of split().']796```797798Иногда нужно знать не только текст между разделителями, но и сам разделитель. Если в RE используются захватывающие круглые скобки, то их значения также возвращаются как часть списка. Сравните следующие вызовы:799800```python801>>> p = re.compile(r'\W+')802>>> p2 = re.compile(r'(\W+)')803>>> p.split('This... is a test.')804['This', 'is', 'a', 'test', '']805>>> p2.split('This... is a test.')806['This', '... ', 'is', ' ', 'a', ' ', 'test', '.', '']807```808809Функция уровня модуля [`re.split()`](https://python-all.ru/2.6/library/re.html#re.split) добавляет регулярное выражение в качестве первого аргумента, но в остальном делает то же самое.810811```python812>>> re.split('[\W]+', 'Words, words, words.')813['Words', 'words', 'words', '']814>>> re.split('([\W]+)', 'Words, words, words.')815['Words', ', ', 'words', ', ', 'words', '.', '']816>>> re.split('[\W]+', 'Words, words, words.', 1)817['Words', 'words, words.']818```819820### Поиск и замена821822Еще одна распространенная задача – найти все совпадения с шаблоном и заменить их другой строкой. Метод `sub()` принимает значение замены, которое может быть строкой или функцией, и обрабатываемую строку.823824#### `.sub(replacement, string[, count=0])`825826Возвращает строку, полученную заменой самых левых непересекающихся совпадений RE в *строке* на замену *replacement*. Если шаблон не найден, *строка* возвращается без изменений.827828Необязательный аргумент *count* – максимальное количество заменяемых вхождений шаблона; *count* должно быть неотрицательным целым числом. Значение по умолчанию 0 означает замену всех вхождений.829830Вот простой пример использования метода `sub()`. Он заменяет названия цветов на слово `colour`:831832```python833>>> p = re.compile( '(blue|white|red)')834>>> p.sub( 'colour', 'blue socks and red shoes')835'colour socks and colour shoes'836>>> p.sub( 'colour', 'blue socks and red shoes', count=1)837'colour socks and red shoes'838```839840Метод `subn()` делает то же самое, но возвращает кортеж из двух элементов, содержащий новую строку и количество выполненных замен:841842```python843>>> p = re.compile( '(blue|white|red)')844>>> p.subn( 'colour', 'blue socks and red shoes')845('colour socks and colour shoes', 2)846>>> p.subn( 'colour', 'no colours at all')847('no colours at all', 0)848```849850Пустые совпадения заменяются только тогда, когда они не примыкают к предыдущему совпадению.851852```python853>>> p = re.compile('x*')854>>> p.sub('-', 'abxd')855'-a-b-d-'856```857858Если *replacement* – строка, все управляющие последовательности с обратной косой чертой в ней обрабатываются. То есть `\n` преобразуется в символ новой строки, `\r` – в символ возврата каретки и так далее. Неизвестные управляющие последовательности, такие как `\j`, остаются без изменений. Обратные ссылки, например `\6`, заменяются подстрокой, соответствующей группе в регулярном выражении. Это позволяет включать фрагменты исходного текста в результирующую строку замены.859860Этот пример находит слово `section`, за которым следует строка, заключенная в `{`, `}`, и заменяет `section` на `subsection`:861862```python863>>> p = re.compile('section{ ( [^}]* ) }', re.VERBOSE)864>>> p.sub(r'subsection{\1}','section{First} section{second}')865'subsection{First} subsection{second}'866```867868Существует также синтаксис для ссылки на именованные группы, определяемый синтаксисом `(?P<name>...)`. `\g<name>` использует подстроку, совпавшую с группой с именем `name`, а `\g<number>` – соответствующий номер группы. Таким образом, `\g<2>` эквивалентно `\2`, но не вызывает неоднозначности в строке замены, такой как `\g<2>0`. (`\20` было бы интерпретировано как ссылка на группу 20, а не как ссылка на группу 2, за которой следует литеральный символ `'0'`.) Следующие подстановки эквивалентны, но используют все три варианта строки замены.869870```python871>>> p = re.compile('section{ (?P<name> [^}]* ) }', re.VERBOSE)872>>> p.sub(r'subsection{\1}','section{First}')873'subsection{First}'874>>> p.sub(r'subsection{\g<1>}','section{First}')875'subsection{First}'876>>> p.sub(r'subsection{\g<name>}','section{First}')877'subsection{First}'878```879880*replacement* также может быть функцией, что даёт ещё больше контроля. Если *replacement* – функция, то она вызывается для каждого неперекрывающегося вхождения *pattern*. При каждом вызове функция получает аргумент `MatchObject` для данного совпадения и может использовать эту информацию для вычисления нужной строки замены и возврата её.881882В следующем примере функция замены переводит десятичные числа в шестнадцатеричные:883884```python885>>> def hexrepl( match ):886...     "Return the hex string for a decimal number"887...     value = int( match.group() )888...     return hex(value)889...890>>> p = re.compile(r'\d+')891>>> p.sub(hexrepl, 'Call 65490 for printing, 49152 for user code.')892'Call 0xffd2 for printing, 0xc000 for user code.'893```894895При использовании функции уровня модуля [`re.sub()`](https://python-all.ru/2.6/library/re.html#re.sub) шаблон передается первым аргументом. Шаблон может быть предоставлен как объект или как строка; если нужно указать флаги регулярного выражения, необходимо либо использовать объект шаблона в качестве первого параметра, либо использовать встроенные модификаторы в строке шаблона, например, `sub("(?i)b+", "x", "bbbb BBBB")` возвращает `'x x'`.896897## Распространенные проблемы898899Регулярные выражения – мощный инструмент для некоторых приложений, но в некоторых аспектах их поведение не интуитивно и иногда они ведут себя не так, как можно было бы ожидать. В этом разделе будут указаны некоторые наиболее распространённые ошибки.900901### Применение строковых методов902903Иногда использование модуля [`re`](https://python-all.ru/2.6/library/re.html#module-re) является ошибкой. Если вы сопоставляете фиксированную строку или одиночный класс символов и не используете никаких возможностей [`re`](https://python-all.ru/2.6/library/re.html#module-re), таких как флаг `IGNORECASE`, то полная мощь регулярных выражений может не понадобиться. У строк есть несколько методов для выполнения операций с фиксированными строками, и они обычно гораздо быстрее, поскольку реализация представляет собой один небольшой цикл на C, оптимизированный для этой цели, вместо большого, более универсального движка регулярных выражений.904905Один из примеров – замена одной фиксированной строки на другую; например, можно заменить `word` на `deed`. `re.sub()` кажется подходящей функцией для этого, но рассмотрите метод `replace()`. Обратите внимание, что `replace()` также заменит `word` внутри слов, превратив `swordfish` в `sdeedfish`, но наивное RE `word` сделало бы то же самое. (Чтобы избежать замены в частях слов, шаблон должен быть `\bword\b`, чтобы требовалось наличие границы слова с обеих сторон от `word`. Это выходит за рамки возможностей `replace()`.)906907Еще одна распространенная задача – удаление всех вхождений одного символа из строки или замена его другим символом. Это можно сделать с помощью чего-то вроде `re.sub('\n', ' ', S)`, но `translate()` способен выполнить обе задачи и будет быстрее любой операции с регулярными выражениями.908909Короче говоря, прежде чем обращаться к модулю [`re`](https://python-all.ru/2.6/library/re.html#module-re), подумайте, можно ли решить вашу задачу с помощью более быстрого и простого строкового метода.910911### match() и search()912913Функция `match()` проверяет только, совпадает ли регулярное выражение в начале строки, тогда как `search()` будет сканировать строку вперед в поисках совпадения. Важно помнить об этом различии. Помните, `match()` сообщит об успешном совпадении только в том случае, если оно начинается с позиции 0; если совпадение не начинается с нуля, `match()` *не* сообщит о нем.914915```python916>>> print re.match('super', 'superstition').span()917(0, 5)918>>> print re.match('super', 'insuperable')919None920```921922С другой стороны, `search()` будет сканировать строку вперед, сообщая о первом найденном совпадении.923924```python925>>> print re.search('super', 'superstition').span()926(0, 5)927>>> print re.search('super', 'insuperable').span()928(2, 7)929```930931Иногда возникает соблазн продолжать использовать [`re.match()`](https://python-all.ru/2.6/library/re.html#re.match) и просто добавить `.*` в начало регулярного выражения. Сопротивляйтесь этому искушению и используйте [`re.search()`](https://python-all.ru/2.6/library/re.html#re.search). Компилятор регулярных выражений выполняет некоторый анализ, чтобы ускорить поиск совпадения. Один из таких анализов определяет, каким должен быть первый символ совпадения; например, шаблон, начинающийся с `Crow`, должен начинаться с `'C'`. Этот анализ позволяет движку быстро просматривать строку в поисках начального символа, пробуя полное совпадение только в том случае, если найден `'C'`.932933Добавление `.*` сводит на нет эту оптимизацию, требуя сканирования до конца строки и последующего возврата для поиска совпадения с остальной частью регулярного выражения. Вместо этого используйте [`re.search()`](https://python-all.ru/2.6/library/re.html#re.search).934935### Жадный и нежадный режимы936937При повторении регулярного выражения, как в `a*`, результирующее действие – поглотить как можно больше шаблона. Этот факт часто подводит при попытке сопоставить пару сбалансированных разделителей, например угловые скобки, окружающие HTML-тег. Наивный шаблон для сопоставления одного HTML-тега не работает из-за жадной природы `.*`.938939```python940>>> s = '<html><head><title>Title</title>'941>>> len(s)94232943>>> print re.match('<.*>', s).span()944(0, 32)945>>> print re.match('<.*>', s).group()946<html><head><title>Title</title>947```948949Регулярное выражение находит `'<'` в `<html>`, и `.*` поглощает остальную часть строки. Однако в регулярном выражении еще осталась часть, и `>` не может совпасть в конце строки, поэтому движку регулярных выражений приходится возвращаться посимвольно, пока он не найдет совпадение для `>`. Итоговое совпадение простирается от `'<'` в `<html>` до `'>'` в `</title>`, что не является желаемым результатом.950951В этом случае решение – использовать нежадные квантификаторы `*?`, `+?`, `??` или `{m,n}?`, которые сопоставляют как можно *меньше* текста. В приведенном выше примере `'>'` пробуется сразу после того, как первое `'<'` совпадает, и когда это не удается, движок продвигается на один символ за раз, повторяя попытку `'>'` на каждом шаге. Это дает правильный результат:952953```python954>>> print re.match('<.*?>', s).group()955<html>956```957958(Заметьте, что разбор HTML или XML с помощью регулярных выражений – дело мучительное. Быстрые и грязные шаблоны справятся с обычными случаями, но у HTML и XML есть особые случаи, которые сломают очевидное регулярное выражение; к тому времени, как вы напишете регулярное выражение, обрабатывающее все возможные случаи, шаблоны станут *очень* сложными. Используйте для таких задач модуль разбора HTML или XML.)959960### Без использования re.VERBOSE961962К этому моменту вы, вероятно, заметили, что регулярные выражения – это очень компактная запись, но они не слишком читаемы. RE умеренной сложности могут превратиться в длинные наборы обратных слешей, скобок и метасимволов, что затрудняет их чтение и понимание.963964Для таких регулярных выражений может быть полезно указать флаг `re.VERBOSE` при компиляции, так как он позволяет форматировать регулярное выражение более наглядно.965966Флаг `re.VERBOSE` имеет несколько эффектов. Пробельные символы в регулярном выражении, которые *не находятся* внутри класса символов, игнорируются. Это означает, что выражение, такое как `dog | cat`, эквивалентно менее читаемому `dog|cat`, но `[a b]` все равно будет соответствовать символам `'a'`, `'b'` или пробелу. Кроме того, можно размещать комментарии внутри регулярного выражения; комментарии начинаются с символа `#` и продолжаются до следующего перевода строки. При использовании с тройными кавычками это позволяет форматировать регулярные выражения более аккуратно:967968```python969pat = re.compile(r"""970 \s*                 # Skip leading whitespace971 (?P<header>[^:]+)   # Header name972 \s* :               # Whitespace, and a colon973 (?P<value>.*?)      # The header's value -- *? used to974                     # lose the following trailing whitespace975 \s*$                # Trailing whitespace to end-of-line976""", re.VERBOSE)977```978979Это гораздо читаемее, чем:980981```python982pat = re.compile(r"\s*(?P<header>[^:]+)\s*:(?P<value>.*?)\s*$")983```984985## Обратная связь986987Регулярные выражения – сложная тема. Помог ли вам этот документ понять их? Были ли части, которые остались неясными, или проблемы, с которыми вы столкнулись и которые не были здесь рассмотрены? Если да, отправьте предложения по улучшению автору.988989Самая полная книга по регулярным выражениям – это, почти наверняка, «Mastering Regular Expressions» Джеффри Фридла, издательство O'Reilly. К сожалению, она полностью сосредоточена на реализациях регулярных выражений в Perl и Java и не содержит никакого материала по Python, поэтому она не пригодится в качестве справочника для программирования на Python. (Первое издание охватывало ныне удаленный модуль Python `regex`, что вам не очень поможет.) Подумайте о том, чтобы взять ее в библиотеке.990991Сноски992993| [\[1\]](https://python-all.ru/2.6/howto/regex.html#id1) | Введено в Python 2.2.2. |994| --- | --- |995