re — Операции с регулярными выражениями

Исходный код: Lib/re.py.


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

Как шаблоны, так и искомые строки могут быть как строками Юникода (str), так и 8-битными строками (bytes). Однако строки Unicode и 8-битные строки нельзя смешивать: то есть нельзя сопоставить строку Unicode с байтовым шаблоном и наоборот; аналогично, при запросе замены строка замены должна быть того же типа, что и шаблон и строка поиска.

В регулярных выражениях символ обратной косой черты ('\') используется для обозначения специальных форм или для того, чтобы позволить использовать специальные символы, не вызывая их специального значения. Это противоречит использованию того же символа в Python для тех же целей в строковых литералах; например, для соответствия литералу обратной косой черты, возможно, придется написать '\\\\' в качестве строки шаблона, поскольку регулярное выражение должно быть \\, а каждая обратная косая черта должна быть выражена как \\ внутри регулярного строкового литерала Python. Также обратите внимание, что любые недопустимые экранирующие последовательности в Python при использовании обратной косой черты в строковых литералах сейчас генерируют DeprecationWarning, а в будущем это станет SyntaxError. Такое поведение будет происходить, даже если это допустимая экранирующая последовательность для регулярного выражения.

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

Важно отметить, что большинство операций с регулярными выражениями доступны в виде функций и методов на уровне модуля compiled regular expressions. Эти функции представляют собой быстрые клавиши, которые не требуют компиляции объекта regex, но в них отсутствуют некоторые параметры тонкой настройки.

См.также

Сторонний модуль regex, который имеет API, совместимый с модулем стандартной библиотеки re, но предлагает дополнительную функциональность и более тщательную поддержку Unicode.

Синтаксис регулярных выражений

Регулярное выражение (или RE) определяет набор строк, которые ему соответствуют; функции этого модуля позволяют проверить, соответствует ли конкретная строка данному регулярному выражению (или соответствует ли данное регулярное выражение конкретной строке, что сводится к одному и тому же).

Регулярные выражения можно объединять, чтобы получить новые регулярные выражения; если A и B являются регулярными выражениями, то AB также является регулярным выражением. В общем случае, если строка p соответствует A, а другая строка q соответствует B, то строка pq будет соответствовать AB. Это верно, если только A или B не содержат операций с низким приоритетом; граничных условий между A и B; или не имеют нумерованных групповых ссылок. Таким образом, сложные выражения могут быть легко построены из более простых примитивных выражений, подобных описанным здесь. За подробностями теории и реализации регулярных выражений обратитесь к книге Фридла [Frie09] или практически к любому учебнику по построению компиляторов.

Далее следует краткое объяснение формата регулярных выражений. Для получения дополнительной информации и более мягкого изложения обратитесь к HOWTO по регулярным выражениям.

Регулярные выражения могут содержать как специальные, так и обычные символы. Большинство обычных символов, например 'A', 'a' или '0', являются простейшими регулярными выражениями; они просто совпадают сами с собой. Обычные символы можно объединять, например, last соответствует строке 'last'. (В остальной части этого раздела мы будем писать RE в this special style, обычно без кавычек, а строки для сопоставления 'in single quotes').

Некоторые символы, например '|' или '(', являются специальными. Специальные символы либо обозначают классы обычных символов, либо влияют на интерпретацию регулярных выражений вокруг них.

Квалификаторы повторения (*, +, ?, {m,n} и т.д.) не могут быть непосредственно вложенными. Это позволяет избежать двусмысленности с нежадным суффиксом модификатора ?, а также с другими модификаторами в других реализациях. Чтобы применить второй повтор к внутреннему повтору, можно использовать круглые скобки. Например, выражение (?:a{6})* соответствует любому кратному из шести символов 'a'.

Специальными символами являются:

.

(Точка.) В режиме по умолчанию этот символ соответствует любому символу, кроме новой строки. Если указан флаг DOTALL, он соответствует любому символу, включая новую строку.

^

(Каретка.) Совпадает с началом строки, а в режиме MULTILINE также совпадает сразу после каждой новой строки.

$

Совпадает с концом строки или непосредственно перед новой строкой в конце строки, а в режиме MULTILINE также совпадает перед новой строкой. foo соответствует как „foo“, так и „foobar“, в то время как регулярное выражение foo$ соответствует только „foo“. Более интересно, что поиск foo.$ в 'foo1\nfoo2\n' соответствует „foo2“ обычно, но „foo1“ в режиме MULTILINE; поиск одного $ в 'foo\n' найдет два (пустых) совпадения: одно непосредственно перед новой строкой, а другое в конце строки.

*

Заставляет результирующий RE соответствовать 0 или более повторениям предыдущего RE, столько повторений, сколько возможно. ab* будет соответствовать „a“, „ab“ или „a“, за которым следует любое количество „b“.

+

Заставляет результирующий RE соответствовать 1 или более повторениям предыдущего RE. ab+ будет соответствовать „a“, за которым следует любое ненулевое количество „b“; не будет соответствовать только „a“.

?

Приводит к тому, что результирующий RE будет соответствовать 0 или 1 повторению предыдущего RE. ab? будет соответствовать либо „a“, либо „ab“.

*?, +?, ??

Квалификаторы '*', '+' и '?' - это все greedy; они сопоставляют как можно больше текста. Иногда такое поведение нежелательно; если RE <.*> сопоставляется с '<a> b <c>', то будет сопоставлена вся строка, а не только '<a>'. Добавление ? после классификатора заставляет его выполнять проверку в режиме non-greedy или minimal; будет сопоставлено как можно меньше символов. Использование RE <.*?> приведет к совпадению только '<a>'.

{m}

Указывает, что должно быть сопоставлено ровно m копий предыдущего RE; меньшее количество совпадений приводит к тому, что весь RE не сопоставляется. Например, a{6} будет соответствовать ровно шести символам 'a', но не пяти.

{m,n}

Заставляет результирующий RE соответствовать от m до n повторений предыдущего RE, стараясь соответствовать как можно большему количеству повторений. Например, a{3,5} будет соответствовать от 3 до 5 символов 'a'. Если опустить m, то нижняя граница будет равна нулю, а если опустить n, то верхняя граница будет бесконечной. Например, a{4,}b будет соответствовать 'aaaab' или тысяче символов 'a', за которыми следует 'b', но не 'aaab'. Запятая не может быть опущена, иначе модификатор будет перепутан с ранее описанной формой.

{m,n}?

Заставляет результирующий RE соответствовать от m до n повторениям предыдущего RE, стараясь соответствовать как можно меньшему числу повторений. Это не жадная версия предыдущего классификатора. Например, для 6-символьной строки 'aaaaaa', a{3,5} будет соответствовать 5 символам 'a', а a{3,5}? будет соответствовать только 3 символам.

\

Либо экранирует специальные символы (позволяя вам сопоставлять символы типа '*', '?' и т.д.), либо сигнализирует о специальной последовательности; специальные последовательности рассматриваются ниже.

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

[]

Используется для обозначения набора символов. В наборе:

  • Символы могут быть перечислены по отдельности, например, [amk] будет соответствовать 'a', 'm' или 'k'.

  • Диапазоны символов можно указать, задав два символа и разделив их знаком '-', например, [a-z] будет соответствовать любой строчной букве ASCII, [0-5][0-9] будет соответствовать всем двузначным числам от 00 до 59, а [0-9A-Fa-f] будет соответствовать любой шестнадцатеричной цифре. Если - экранирован (например, [a\-z]) или помещен как первый или последний символ (например, [-a] или [a-]), он будет соответствовать литералу '-'.

  • Специальные символы теряют свое специальное значение внутри наборов. Например, [(+*)] будет соответствовать любому из литеральных символов '(', '+', '*' или ')'.

  • Классы символов, такие как \w или \S (определение дано ниже), также принимаются внутри набора, хотя символы, которым они соответствуют, зависят от того, действует ли режим ASCII или LOCALE.

  • Символы, не входящие в диапазон, могут быть сопоставлены с помощью complementing набора. Если первым символом набора является '^', то будут сопоставлены все символы, которые не входят в набор. Например, [^5] будет соответствовать любой символ, кроме '5', а [^^] будет соответствовать любой символ, кроме '^'. ^ не имеет специального значения, если это не первый символ в наборе.

  • Для соответствия литералу ']' внутри набора, предварите его обратной косой чертой или поместите в начало набора. Например, и [()[\]{}], и []()[{}] будут соответствовать скобке.

  • В будущем может быть добавлена поддержка вложенных множеств и операций с множествами, как в Unicode Technical Standard #18. Это изменит синтаксис, поэтому для облегчения этого изменения пока что в неоднозначных случаях будет ставиться FutureWarning. К ним относятся наборы, начинающиеся с литерала '[' или содержащие последовательности символов '--', '&&', '~~' и '||'. Чтобы избежать предупреждения, экранируйте их обратной косой чертой.

Изменено в версии 3.7: FutureWarning возникает, если набор символов содержит конструкции, которые в будущем семантически изменятся.

|

A|B, где A и B могут быть произвольными RE, создает регулярное выражение, которое будет соответствовать либо A, либо B. Таким образом, произвольное количество RE может быть разделено '|'. Это можно использовать и внутри групп (см. ниже). По мере сканирования целевой строки RE, разделенные '|', перебираются слева направо. Когда один шаблон полностью совпадает, эта ветвь принимается. Это означает, что если A совпадает, то B не будет проверяться дальше, даже если это даст более длинное общее совпадение. Другими словами, оператор '|' никогда не бывает жадным. Для проверки литерала '|' используйте \| или заключите его внутри класса символов, как в [|].

(...)

Совпадает с любым регулярным выражением, находящимся внутри круглых скобок, и указывает на начало и конец группы; содержимое группы может быть получено после выполнения совпадения и может быть сопоставлено далее в строке с помощью специальной последовательности \number, описанной ниже. Для сопоставления литералов '(' или ')' используйте \( или \), или заключите их в класс символов: [(], [)].

(?...)

Это расширительная нотация ('?', следующий за '(', не имеет смысла). Первый символ после '?' определяет значение и дальнейший синтаксис конструкции. Расширения обычно не создают новую группу; (?P<name>...) является единственным исключением из этого правила. Ниже перечислены поддерживаемые в настоящее время расширения.

(?aiLmsux)

(Одна или несколько букв из набора 'a', 'i', 'L', 'm', 's', 'u', 'x'). Группа соответствует пустой строке; буквы устанавливают соответствующие флаги: re.A (соответствие только ASCII), re.I (игнорирование регистра), re.L (зависит от локали), re.M (многострочный), re.S (точка соответствует всем), re.U (соответствие Unicode) и re.X (многословный), для всего регулярного выражения. (Флаги описаны в Содержание модуля.) Это удобно, если вы хотите включить флаги как часть регулярного выражения, вместо того, чтобы передавать аргумент flag функции re.compile(). Флаги должны использоваться первыми в строке выражения.

(?:...)

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

(?aiLmsux-imsx:...)

(Ноль или более букв из набора 'a', 'i', 'L', 'm', 's', 'u', 'x', опционально следует '-', за которой следует одна или более букв из набора 'i', 'm', 's', 'x'). Буквы устанавливают или снимают соответствующие флаги: re.A (соответствие только ASCII), re.I (игнорирование регистра), re.L (зависит от локали), re.M (многострочный), re.S (точка соответствует всем), re.U (соответствие Unicode) и re.X (многословный) для части выражения. (Флаги описаны в Содержание модуля).

Буквы 'a', 'L' и 'u' являются взаимоисключающими при использовании в качестве инлайн-флагов, поэтому они не могут комбинироваться или следовать за '-'. Вместо этого, когда один из них появляется в инлайн-группе, он переопределяет режим соответствия в объемлющей группе. В шаблонах Юникода (?a:...) переключается на соответствие только ASCII, а (?u:...) - на соответствие Юникоду (по умолчанию). В байтовых шаблонах (?L:...) переключается на соответствие в зависимости от локали, а (?a:...) переключается на соответствие только ASCII (по умолчанию). Это переопределение действует только для узкой инлайн-группы, за пределами группы восстанавливается исходный режим соответствия.

Добавлено в версии 3.6.

Изменено в версии 3.7: Буквы 'a', 'L' и 'u' также могут использоваться в группе.

(?P<name>...)

Аналогично регулярным круглым скобкам, но подстрока, совпадающая с группой, доступна через символическое имя группы name. Имена групп должны быть действительными идентификаторами Python, и каждое имя группы должно быть определено только один раз в регулярном выражении. Символьная группа также является нумерованной группой, как если бы группа не имела имени.

На именованные группы можно ссылаться в трех контекстах. Если шаблон (?P<quote>['"]).*?(?P=quote) (т.е. соответствует строке, заключенной в одинарные или двойные кавычки):

Контекст ссылки на группу «цитата»

Способы ссылки

по той же схеме

  • (?P=quote) (как показано на рисунке)

  • \1

при обработке объекта соответствия m

  • m.group('quote')

  • m.end('quote') (и т.д.)

в строке, переданной в аргумент repl команды re.sub().

  • \g<quote>

  • \g<1>

  • \1

(?P=name)

Обратная ссылка на именованную группу; она соответствует тексту, которому соответствовала предыдущая группа с именем name.

(?#...)

Комментарий; содержимое круглых скобок просто игнорируется.

(?=...)

Совпадает, если ... совпадает со следующим, но не поглощает ни одной строки. Это называется lookahead assertion. Например, Isaac (?=Asimov) будет соответствовать 'Isaac ' только если за ним следует 'Asimov'.

(?!...)

Совпадает, если ... не совпадает со следующим. Это negative lookahead assertion. Например, Isaac (?!Asimov) будет соответствовать 'Isaac ' только если за ним не следует 'Asimov'.

(?<=...)

Устанавливает соответствие, если текущей позиции в строке предшествует соответствие для ..., которое заканчивается на текущей позиции. Это называется positive lookbehind assertion. (?<=abc)def найдет совпадение в 'abcdef', так как lookbehind отступит на 3 символа назад и проверит, совпадает ли содержащийся шаблон. Содержащийся шаблон должен соответствовать только строкам некоторой фиксированной длины, то есть abc или a|b допустимы, а a* и a{3,4} - нет. Обратите внимание, что шаблоны, начинающиеся с положительных утверждений lookbehind, не будут совпадать с началом искомой строки; скорее всего, вы захотите использовать функцию search(), а не match():

>>> import re
>>> m = re.search('(?<=abc)def', 'abcdef')
>>> m.group(0)
'def'

Этот пример ищет слово, следующее за дефисом:

>>> m = re.search(r'(?<=-)\w+', 'spam-egg')
>>> m.group(0)
'egg'

Изменено в версии 3.5: Добавлена поддержка групповых ссылок фиксированной длины.

(?<!...)

Устанавливает соответствие, если текущей позиции в строке не предшествует соответствие .... Это называется negative lookbehind assertion. Как и в случае с утверждениями положительного поиска, содержащийся шаблон должен совпадать только со строками некоторой фиксированной длины. Шаблоны, начинающиеся с отрицательных утверждений lookbehind, могут совпадать с началом искомой строки.

(?(id/name)yes-pattern|no-pattern)

Попытается найти соответствие с yes-pattern, если группа с заданным id или name существует, и с no-pattern, если не существует. no-pattern является необязательным и может быть опущен. Например, (<)?(\w+@\w+(?:\.\w+)+)(?(1)>|$) - это плохой шаблон для сопоставления с электронной почтой, который будет соответствовать '<user@host.com>', а также 'user@host.com', но не соответствовать '<user@host.com' и 'user@host.com>'.

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

\number

Совпадает с содержимым группы с тем же номером. Группы нумеруются, начиная с 1. Например, (.+) \1 соответствует 'the the' или '55 55', но не 'thethe' (обратите внимание на пробел после группы). Эта специальная последовательность может быть использована только для соответствия одной из первых 99 групп. Если первая цифра числа равна 0, или число имеет длину 3 восьмеричных разряда, оно будет интерпретировано не как соответствие группе, а как символ с восьмеричным значением числа. Внутри '[' и ']' класса символов все числовые символы рассматриваются как символы.

\A

Совпадает только с началом строки.

\b

Сопоставляет пустую строку, но только в начале или конце слова. Слово определяется как последовательность символов слова. Обратите внимание, что формально \b определяется как граница между символами \w и \W (или наоборот), или между \w и началом/концом строки. Это означает, что r'\bfoo\b' соответствует 'foo', 'foo.', '(foo)', 'bar foo baz', но не 'foobar' или 'foo3'.

По умолчанию буквенно-цифровые обозначения Unicode соответствуют тем, которые используются в шаблонах Unicode, но это можно изменить с помощью флага ASCII. Границы слов определяются текущей локалью, если используется флаг LOCALE. Внутри диапазона символов \b представляет символ обратного пробела, для совместимости со строковыми литералами Python.

\B

Совпадает с пустой строкой, но только если она не находится в начале или конце слова. Это означает, что r'py\B' соответствует 'python', 'py3', 'py2', но не 'py', 'py.' или 'py!'. \B является противоположностью \b, поэтому символами слов в шаблонах Юникода являются алфавитно-цифровые символы Юникода или символ подчеркивания, хотя это можно изменить с помощью флага ASCII. Границы слов определяются текущей локалью, если используется флаг LOCALE.

\d
Для шаблонов Unicode (str):

Сопоставляет любую десятичную цифру Юникода (то есть любой символ в категории символов Юникода [Nd]). Это включает [0-9], а также многие другие цифровые символы. Если используется флаг ASCII, то совпадает только [0-9].

Для 8-битных (байтов) шаблонов:

Совпадает с любой десятичной цифрой; это эквивалентно [0-9].

\D

Сопоставляет любой символ, не являющийся десятичной цифрой. Это противоположность \d. Если используется флаг ASCII, это становится эквивалентом [^0-9].

\s
Для шаблонов Unicode (str):

Сопоставляет символы пробелов Unicode (к ним относятся [ \t\n\r\f\v], а также многие другие символы, например, неразрывные пробелы, предписанные правилами типографики во многих языках). Если используется флаг ASCII, сопоставляются только [ \t\n\r\f\v].

Для 8-битных (байтов) шаблонов:

Сопоставляет символы, считающиеся пробелами в наборе символов ASCII; это эквивалентно [ \t\n\r\f\v].

\S

Сопоставляет любой символ, который не является символом пробела. Это противоположность \s. Если используется флаг ASCII, это становится эквивалентом [^ \t\n\r\f\v].

\w
Для шаблонов Unicode (str):

Сопоставляет символы слов Unicode; сюда входит большинство символов, которые могут быть частью слова в любом языке, а также цифры и символ подчеркивания. Если используется флаг ASCII, сопоставляются только [a-zA-Z0-9_].

Для 8-битных (байтов) шаблонов:

Совпадает с символами, считающимися буквенно-цифровыми в наборе символов ASCII; это эквивалентно [a-zA-Z0-9_]. Если используется флаг LOCALE, совпадают символы, считающиеся буквенно-цифровыми в текущей локали, и символ подчеркивания.

\W

Сопоставляет любой символ, который не является символом слова. Это противоположность \w. Если используется флаг ASCII, это становится эквивалентом [^a-zA-Z0-9_]. Если используется флаг LOCALE, совпадают символы, которые не являются ни буквенно-цифровыми в текущей локали, ни символами подчеркивания.

\Z

Выполняет поиск только в конце строки.

Большинство стандартных экранов, поддерживаемых строковыми литералами Python, также принимаются синтаксическим анализатором регулярных выражений:

\a      \b      \f      \n
\N      \r      \t      \u
\U      \v      \x      \\

(Обратите внимание, что \b используется для обозначения границ слов и означает «backspace» только внутри классов символов).

Управляющие последовательности '\u', '\U' и '\N' распознаются только в шаблонах Unicode. В байтовых шаблонах они являются ошибками. Неизвестные экранирующие последовательности букв ASCII зарезервированы для будущего использования и рассматриваются как ошибки.

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

Изменено в версии 3.3: Добавлены управляющие последовательности '\u' и '\U'.

Изменено в версии 3.6: Неизвестные эскейпы, состоящие из '\' и буквы ASCII, теперь являются ошибками.

Изменено в версии 3.8: Добавлена управляющая последовательность '\N{name}'. Как и в строковых литералах, она расширяется до именованного символа Unicode (например, '\N{EM DASH}').

Содержание модуля

Модуль определяет несколько функций, констант и исключение. Некоторые из функций являются упрощенными версиями полнофункциональных методов для скомпилированных регулярных выражений. Большинство нетривиальных приложений всегда используют скомпилированную форму.

Флаги

Изменено в версии 3.6: Константы флагов теперь являются экземплярами RegexFlag, который является подклассом enum.IntFlag.

re.A
re.ASCII

Заставьте \w, \W, \b, \B, \d, \D, \s и \S выполнять сопоставление только ASCII вместо полного сопоставления Unicode. Это имеет смысл только для шаблонов Unicode и игнорируется для байтовых шаблонов. Соответствует флагу инлайна (?a).

Обратите внимание, что для обратной совместимости флаг re.U все еще существует (как и его синоним re.UNICODE и его встроенный аналог (?u)), но в Python 3 они излишни, так как по умолчанию для строк используется Юникод (а для байтов Юникод не разрешен).

re.DEBUG

Отображение отладочной информации о скомпилированном выражении. Нет соответствующего встроенного флага.

re.I
re.IGNORECASE

Выполняйте сопоставление без учета регистра; выражения типа [A-Z] также будут соответствовать строчным буквам. Полное соответствие Юникоду (например, Ü соответствует ü) также работает, если не используется флаг re.ASCII для отключения соответствий, не относящихся к ASCII. Текущая локаль не изменяет действие этого флага, если только не используется также флаг re.LOCALE. Соответствует инлайн-флагу (?i).

Обратите внимание, что если шаблоны Юникода [a-z] или [A-Z] используются в сочетании с флагом IGNORECASE, то они соответствуют 52 буквам ASCII и 4 дополнительным не ASCII буквам: „İ“ (U+0130, латинская заглавная буква I с точкой вверху), „ı“ (U+0131, латинская строчная буква без точки i), „ſ“ (U+017F, латинская строчная буква длинная s) и „K“ (U+212A, знак Кельвина). Если используется флаг ASCII, то сопоставляются только буквы от „a“ до „z“ и от „A“ до „Z“.

re.L
re.LOCALE

Сделать \w, \W, \b, \B и нечувствительное к регистру сопоставление зависимым от текущей локали. Этот флаг можно использовать только с байтовыми шаблонами. Использование этого флага не рекомендуется, так как механизм локалей очень ненадежен, он обрабатывает только одну «культуру» за раз, и работает только с 8-битными локалями. В Python 3 по умолчанию уже включено сопоставление с Юникодом для шаблонов Юникода (str), и оно может работать с различными локалями/языками. Соответствует инлайн-флагу (?L).

Изменено в версии 3.6: re.LOCALE может использоваться только с байтовыми шаблонами и не совместим с re.ASCII.

Изменено в версии 3.7: Скомпилированные объекты регулярных выражений с флагом re.LOCALE больше не зависят от локали во время компиляции. Только локаль во время сопоставления влияет на результат сопоставления.

re.M
re.MULTILINE

Если символ шаблона '^' указан, то он совпадает в начале строки и в начале каждой строки (сразу после каждой новой строки); а символ шаблона '$' совпадает в конце строки и в конце каждой строки (сразу перед каждой новой строкой). По умолчанию '^' совпадает только в начале строки, а '$' - только в конце строки и непосредственно перед новой строкой (если она есть) в конце строки. Соответствует флагу инлайн (?m).

re.S
re.DOTALL

Заставить специальный символ '.' соответствовать любому символу, включая новую строку; без этого флага '.' будет соответствовать всему, кроме новой строки. Соответствует флагу инлайн (?s).

re.X
re.VERBOSE

Этот флаг позволяет писать регулярные выражения, которые выглядят красивее и читабельнее, позволяя визуально разделять логические разделы шаблона и добавлять комментарии. Пробелы внутри шаблона игнорируются, за исключением случаев, когда они находятся в классе символов, или когда им предшествует обратная косая черта без символов, или внутри лексем типа *?, (?: или (?P<...>. Если строка содержит символ #, который не входит в класс символов и которому не предшествует обратная косая черта, все символы от самого левого такого символа # до конца строки игнорируются.

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

a = re.compile(r"""\d +  # the integral part
                   \.    # the decimal point
                   \d *  # some fractional digits""", re.X)
b = re.compile(r"\d+\.\d*")

Соответствует флагу инлайн (?x).

Функции

re.compile(pattern, flags=0)

Компилируйте шаблон регулярного выражения в regular expression object, который можно использовать для сопоставления с помощью его match(), search() и других методов, описанных ниже.

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

Последовательность

prog = re.compile(pattern)
result = prog.match(string)

эквивалентно

result = re.match(pattern, string)

но использование re.compile() и сохранение полученного объекта регулярного выражения для повторного использования более эффективно, когда выражение будет использоваться несколько раз в одной программе.

Примечание

Скомпилированные версии последних шаблонов, переданных в re.compile() и функции соответствия на уровне модуля, кэшируются, поэтому программам, которые используют только несколько регулярных выражений за раз, не нужно беспокоиться о компиляции регулярных выражений.

re.search(pattern, string, flags=0)

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

re.match(pattern, string, flags=0)

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

Обратите внимание, что даже в режиме MULTILINE, re.match() будет совпадать только в начале строки, а не в начале каждой строки.

Если вы хотите найти совпадение в любом месте строки, используйте search() (см. также поиск() против совпадения()).

re.fullmatch(pattern, string, flags=0)

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

Добавлено в версии 3.4.

re.split(pattern, string, maxsplit=0, flags=0)

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

>>> re.split(r'\W+', 'Words, words, words.')
['Words', 'words', 'words', '']
>>> re.split(r'(\W+)', 'Words, words, words.')
['Words', ', ', 'words', ', ', 'words', '.', '']
>>> re.split(r'\W+', 'Words, words, words.', 1)
['Words', 'words, words.']
>>> re.split('[a-f]+', '0a3B9', flags=re.IGNORECASE)
['0', '3', '9']

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

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

Таким образом, компоненты разделителя всегда находятся под одинаковыми относительными индексами в списке результатов.

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

>>> re.split(r'\b', 'Words, words, words.')
['', 'Words', ', ', 'words', ', ', 'words', '.']
>>> re.split(r'\W*', '...words...')
['', '', 'w', 'o', 'r', 'd', 's', '', '']
>>> re.split(r'(\W*)', '...words...')
['', '...', '', '', 'w', '', 'o', '', 'r', '', 'd', '', 's', '...', '', '', '']

Изменено в версии 3.1: Добавлен необязательный аргумент flags.

Изменено в версии 3.7: Добавлена поддержка разбиения по шаблону, который может соответствовать пустой строке.

re.findall(pattern, string, flags=0)

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

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

>>> re.findall(r'\bf[a-z]*', 'which foot or hand fell fastest')
['foot', 'fell', 'fastest']
>>> re.findall(r'(\w+)=(\d+)', 'set width=20 and height=10')
[('width', '20'), ('height', '10')]

Изменено в версии 3.7: Непустые совпадения теперь могут начинаться сразу после предыдущего пустого совпадения.

re.finditer(pattern, string, flags=0)

Возвращает iterator, дающий match objects по всем непересекающимся совпадениям для RE шаблона в строке. Строка* сканируется слева направо, и совпадения возвращаются в найденном порядке. Пустые совпадения включаются в результат.

Изменено в версии 3.7: Непустые совпадения теперь могут начинаться сразу после предыдущего пустого совпадения.

re.sub(pattern, repl, string, count=0, flags=0)

Возвращает строку, полученную путем замены крайних левых непересекающихся вхождений шаблона в строке на замену repl. Если шаблон не найден, строка возвращается без изменений. repl может быть строкой или функцией; если это строка, то все обратные косые черты в ней обрабатываются. То есть, \n преобразуется в один символ новой строки, \r преобразуется в возврат каретки, и так далее. Неизвестные эскапады букв ASCII резервируются для будущего использования и рассматриваются как ошибки. Другие неизвестные эскапады, такие как \&, оставляются в покое. Обратные ссылки, такие как \6, заменяются подстрокой, совпадающей с группой 6 в шаблоне. Например:

>>> re.sub(r'def\s+([a-zA-Z_][a-zA-Z_0-9]*)\s*\(\s*\):',
...        r'static PyObject*\npy_\1(void)\n{',
...        'def myfunc():')
'static PyObject*\npy_myfunc(void)\n{'

Если repl является функцией, то она вызывается для каждого непересекающегося вхождения шаблона. Функция принимает один аргумент match object и возвращает заменяющую строку. Например:

>>> def dashrepl(matchobj):
...     if matchobj.group(0) == '-': return ' '
...     else: return '-'
>>> re.sub('-{1,2}', dashrepl, 'pro----gram-files')
'pro--gram files'
>>> re.sub(r'\sAND\s', ' & ', 'Baked Beans And Spam', flags=re.IGNORECASE)
'Baked Beans & Spam'

Шаблон может быть строкой или pattern object.

Необязательный аргумент count - это максимальное количество заменяемых вхождений шаблона; count должно быть неотрицательным целым числом. Если он опущен или равен нулю, то заменяются все вхождения. Пустые вхождения шаблона заменяются только тогда, когда они не примыкают к предыдущему пустому вхождению, поэтому sub('x*', '-', 'abxd') возвращает '-a-b--d-'.

В строковых аргументах типа repl, в дополнение к описанным выше экранированию символов и обратным ссылкам, \g<name> будет использоваться подстрока, соответствующая группе с именем name, как определено синтаксисом (?P<name>...). \g<number> использует соответствующий номер группы; \g<2> поэтому эквивалентен \2, но не является двусмысленным в такой замене, как \g<2>0. \20 будет интерпретироваться как ссылка на группу 20, а не как ссылка на группу 2, за которой следует литеральный символ '0'. Обратная ссылка \g<0> заменяет всю подстроку, совпадающую с RE.

Изменено в версии 3.1: Добавлен необязательный аргумент flags.

Изменено в версии 3.5: Несовпадающие группы заменяются пустой строкой.

Изменено в версии 3.6: Неизвестные эскейпы в pattern, состоящие из '\' и буквы ASCII, теперь являются ошибкой.

Изменено в версии 3.7: Неизвестные эскейпы в repl, состоящие из '\' и буквы ASCII, теперь являются ошибкой.

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

re.subn(pattern, repl, string, count=0, flags=0)

Выполните ту же операцию, что и sub(), но верните кортеж (new_string, number_of_subs_made).

Изменено в версии 3.1: Добавлен необязательный аргумент flags.

Изменено в версии 3.5: Несовпадающие группы заменяются пустой строкой.

re.escape(pattern)

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

>>> print(re.escape('https://www.python.org'))
https://www\.python\.org

>>> legal_chars = string.ascii_lowercase + string.digits + "!#$%&'*+-.^_`|~:"
>>> print('[%s]+' % re.escape(legal_chars))
[abcdefghijklmnopqrstuvwxyz0123456789!\#\$%\&'\*\+\-\.\^_`\|\~:]+

>>> operators = ['+', '-', '*', '/', '**']
>>> print('|'.join(map(re.escape, sorted(operators, reverse=True))))
/|\-|\+|\*\*|\*

Эта функция не должна использоваться для замены строки в sub() и subn(), следует экранировать только обратные слеши. Например:

>>> digits_re = r'\d+'
>>> sample = '/usr/sbin/sendmail - 0 errors, 12 warnings'
>>> print(re.sub(digits_re, digits_re.replace('\\', r'\\'), sample))
/usr/sbin/sendmail - \d+ errors, \d+ warnings

Изменено в версии 3.3: Символ '_' больше не экранируется.

Изменено в версии 3.7: Только символы, которые могут иметь специальное значение в регулярном выражении, экранируются. В результате, '!', '"', '%', "'", ',', '/', ':', ';', '<', '=', '>', '@' и "`" больше не экранируются.

re.purge()

Очистить кэш регулярных выражений.

Исключения

exception re.error(msg, pattern=None, pos=None)

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

msg

Неформатированное сообщение об ошибке.

pattern

Шаблон регулярного выражения.

pos

Индекс в шаблоне, где произошла ошибка компиляции (может быть None).

lineno

Строка, соответствующая pos (может быть None).

colno

Столбец, соответствующий pos (может быть None).

Изменено в версии 3.5: Добавлены дополнительные атрибуты.

Объекты регулярных выражений

Скомпилированные объекты регулярных выражений поддерживают следующие методы и атрибуты:

Pattern.search(string[, pos[, endpos]])

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

Необязательный второй параметр pos задает индекс в строке, с которого должен начинаться поиск; по умолчанию он равен 0. Это не совсем эквивалентно разрезанию строки; символ шаблона '^' совпадает с реальным началом строки и позициями сразу после новой строки, но не обязательно с индексом, с которого начинается поиск.

Необязательный параметр endpos ограничивает расстояние поиска строки; будет считаться, что длина строки составляет endpos символов, поэтому поиск совпадения будет производиться только в символах от pos до endpos - 1. Если endpos меньше pos, совпадение не будет найдено; в противном случае, если rx - скомпилированный объект регулярного выражения, rx.search(string, 0, 50) будет эквивалентно rx.search(string[:50], 0).

>>> pattern = re.compile("d")
>>> pattern.search("dog")     # Match at index 0
<re.Match object; span=(0, 1), match='d'>
>>> pattern.search("dog", 1)  # No match; search doesn't include the "d"
Pattern.match(string[, pos[, endpos]])

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

Необязательные параметры pos и endpos имеют то же значение, что и для метода search().

>>> pattern = re.compile("o")
>>> pattern.match("dog")      # No match as "o" is not at the start of "dog".
>>> pattern.match("dog", 1)   # Match as "o" is the 2nd character of "dog".
<re.Match object; span=(1, 2), match='o'>

Если вы хотите найти совпадение в любом месте строки, используйте search() (см. также поиск() против совпадения()).

Pattern.fullmatch(string[, pos[, endpos]])

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

Необязательные параметры pos и endpos имеют то же значение, что и для метода search().

>>> pattern = re.compile("o[gh]")
>>> pattern.fullmatch("dog")      # No match as "o" is not at the start of "dog".
>>> pattern.fullmatch("ogre")     # No match as not the full string matches.
>>> pattern.fullmatch("doggie", 1, 3)   # Matches within given limits.
<re.Match object; span=(1, 3), match='og'>

Добавлено в версии 3.4.

Pattern.split(string, maxsplit=0)

Идентична функции split(), использует скомпилированный шаблон.

Pattern.findall(string[, pos[, endpos]])

Аналогична функции findall(), использует скомпилированный шаблон, но также принимает необязательные параметры pos и endpos, которые ограничивают область поиска, как для search().

Pattern.finditer(string[, pos[, endpos]])

Аналогична функции finditer(), использует скомпилированный шаблон, но также принимает необязательные параметры pos и endpos, которые ограничивают область поиска, как для search().

Pattern.sub(repl, string, count=0)

Идентична функции sub(), использует скомпилированный шаблон.

Pattern.subn(repl, string, count=0)

Идентична функции subn(), использует скомпилированный шаблон.

Pattern.flags

Флаги соответствия regex. Это комбинация флагов, заданных в compile(), любых встроенных флагов (?...) в шаблоне, а также неявных флагов, таких как UNICODE, если шаблон является строкой Unicode.

Pattern.groups

Количество групп захвата в детали.

Pattern.groupindex

Словарь, отображающий любые символические имена групп, определенные (?P<id>), на номера групп. Словарь пуст, если в шаблоне не использовались символьные группы.

Pattern.pattern

Строка шаблона, из которой был скомпилирован объект шаблона.

Изменено в версии 3.7: Добавлена поддержка copy.copy() и copy.deepcopy(). Скомпилированные объекты регулярных выражений считаются атомарными.

Соответствие объектов

Объекты Match всегда имеют булево значение True. Поскольку match() и search() возвращают None при отсутствии совпадения, вы можете проверить, было ли совпадение, с помощью простого оператора if:

match = re.search(pattern, string)
if match:
    process(match)

Объекты соответствия поддерживают следующие методы и атрибуты:

Match.expand(template)

Возвращает строку, полученную при выполнении подстановки обратных косых черт в строку шаблона template, как это делается методом sub(). Обратные косые черты, такие как \n, преобразуются в соответствующие символы, а числовые обратные ссылки (\1, \2) и именованные обратные ссылки (\g<1>, \g<name>) заменяются содержимым соответствующей группы.

Изменено в версии 3.5: Несовпадающие группы заменяются пустой строкой.

Match.group([group1, ...])

Возвращает одну или несколько подгрупп совпадения. Если аргумент один, результатом будет одна строка; если аргументов несколько, результатом будет кортеж с одним элементом на аргумент. Без аргументов значение group1 по умолчанию равно нулю (возвращается все совпадение). Если аргумент groupN равен нулю, то возвращаемое значение - это вся совпадающая строка; если он находится в диапазоне [1…99], то это строка, совпадающая с соответствующей группой, заключенной в скобки. Если номер группы отрицательный или больше, чем количество групп, определенных в шаблоне, то выдается исключение IndexError. Если группа содержится в части шаблона, которая не совпала, то результатом будет None. Если группа содержится в части шаблона, которая совпала несколько раз, возвращается последнее совпадение.

>>> m = re.match(r"(\w+) (\w+)", "Isaac Newton, physicist")
>>> m.group(0)       # The entire match
'Isaac Newton'
>>> m.group(1)       # The first parenthesized subgroup.
'Isaac'
>>> m.group(2)       # The second parenthesized subgroup.
'Newton'
>>> m.group(1, 2)    # Multiple arguments give us a tuple.
('Isaac', 'Newton')

Если в регулярном выражении используется синтаксис (?P<name>...), аргументы groupN также могут быть строками, идентифицирующими группы по имени группы. Если строковый аргумент не используется в шаблоне в качестве имени группы, то возникает исключение IndexError.

Умеренно сложный пример:

>>> m = re.match(r"(?P<first_name>\w+) (?P<last_name>\w+)", "Malcolm Reynolds")
>>> m.group('first_name')
'Malcolm'
>>> m.group('last_name')
'Reynolds'

На именованные группы можно также ссылаться по их индексу:

>>> m.group(1)
'Malcolm'
>>> m.group(2)
'Reynolds'

Если группа совпадает несколько раз, доступно только последнее совпадение:

>>> m = re.match(r"(..)+", "a1b2c3")  # Matches 3 times.
>>> m.group(1)                        # Returns only the last match.
'c3'
Match.__getitem__(g)

Эта функция идентична m.group(g). Это позволяет легче получить доступ к отдельной группе из совпадения:

>>> m = re.match(r"(\w+) (\w+)", "Isaac Newton, physicist")
>>> m[0]       # The entire match
'Isaac Newton'
>>> m[1]       # The first parenthesized subgroup.
'Isaac'
>>> m[2]       # The second parenthesized subgroup.
'Newton'

Добавлено в версии 3.6.

Match.groups(default=None)

Возвращает кортеж, содержащий все подгруппы совпадения, начиная с 1 и заканчивая количеством групп в шаблоне. Аргумент default используется для групп, которые не участвовали в совпадении; по умолчанию он равен None.

Например:

>>> m = re.match(r"(\d+)\.(\d+)", "24.1632")
>>> m.groups()
('24', '1632')

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

>>> m = re.match(r"(\d+)\.?(\d+)?", "24")
>>> m.groups()      # Second group defaults to None.
('24', None)
>>> m.groups('0')   # Now, the second group defaults to '0'.
('24', '0')
Match.groupdict(default=None)

Возвращает словарь, содержащий все именованные подгруппы совпадения, с ключом по имени подгруппы. Аргумент default используется для групп, которые не участвовали в совпадении; по умолчанию он равен None. Например:

>>> m = re.match(r"(?P<first_name>\w+) (?P<last_name>\w+)", "Malcolm Reynolds")
>>> m.groupdict()
{'first_name': 'Malcolm', 'last_name': 'Reynolds'}
Match.start([group])
Match.end([group])

Возвращает индексы начала и конца подстроки, совпадающей с group; group по умолчанию равна нулю (означает всю совпадающую подстроку). Верните -1, если group существует, но не участвует в совпадении. Для объекта совпадения m и группы g, которая внесла свой вклад в совпадение, подстрока, совпавшая с группой g (эквивалентно m.group(g)), будет

m.string[m.start(g):m.end(g)]

Обратите внимание, что m.start(group) будет равно m.end(group), если group соответствует нулевой строке. Например, после m = re.search('b(c?)', 'cba'), m.start(0) будет 1, m.end(0) будет 2, m.start(1) и m.end(1) будут оба 2, а m.start(2) вызовет исключение IndexError.

Пример, который удалит remove_this из адресов электронной почты:

>>> email = "tony@tiremove_thisger.net"
>>> m = re.search("remove_this", email)
>>> email[:m.start()] + email[m.end():]
'tony@tiger.net'
Match.span([group])

Для совпадения m возвращается 2-кортеж (m.start(group), m.end(group)). Обратите внимание, что если group не участвовала в совпадении, то это (-1, -1). По умолчанию group равна нулю, то есть весь матч.

Match.pos

Значение pos, которое было передано в метод search() или match() метода regex object. Это индекс строки, по которому механизм RE начал поиск совпадения.

Match.endpos

Значение endpos, которое было передано в метод search() или match() метода regex object. Это индекс строки, дальше которого механизм RE не пойдет.

Match.lastindex

Целочисленный индекс последней совпавшей группы захвата, или None, если не было найдено ни одной группы. Например, выражения (a)b, ((a)(b)) и ((ab)) будут иметь lastindex == 1, если их применить к строке 'ab', а выражение (a)(b) будет иметь lastindex == 2, если его применить к той же строке.

Match.lastgroup

Имя последней совпавшей группы захвата, или None, если у группы не было имени, или если вообще не было найдено ни одной группы.

Match.re

regular expression object, чей метод match() или search() произвел данный экземпляр совпадения.

Match.string

Строка, переданная в match() или search().

Изменено в версии 3.7: Добавлена поддержка copy.copy() и copy.deepcopy(). Объекты соответствия считаются атомарными.

Примеры регулярных выражений

Проверка наличия пары

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

def displaymatch(match):
    if match is None:
        return None
    return '<Match: %r, groups=%r>' % (match.group(), match.groups())

Предположим, вы пишете программу для игры в покер, где рука игрока представлена в виде строки из 5 символов, где каждый символ обозначает карту, «a» - туз, «k» - король, «q» - королева, «j» - валет, «t» - 10, а от «2» до «9» обозначают карту с этим значением.

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

>>> valid = re.compile(r"^[a2-9tjqk]{5}$")
>>> displaymatch(valid.match("akt5q"))  # Valid.
"<Match: 'akt5q', groups=()>"
>>> displaymatch(valid.match("akt5e"))  # Invalid.
>>> displaymatch(valid.match("akt"))    # Invalid.
>>> displaymatch(valid.match("727ak"))  # Valid.
"<Match: '727ak', groups=()>"

Эта последняя рука, "727ak", содержала пару или две одинаковые по значению карты. Для того, чтобы сопоставить это с регулярным выражением, можно использовать обратные ссылки, например:

>>> pair = re.compile(r".*(.).*\1")
>>> displaymatch(pair.match("717ak"))     # Pair of 7s.
"<Match: '717', groups=('7',)>"
>>> displaymatch(pair.match("718ak"))     # No pairs.
>>> displaymatch(pair.match("354aa"))     # Pair of aces.
"<Match: '354aa', groups=('a',)>"

Чтобы узнать, из каких карт состоит пара, можно использовать метод group() объекта match следующим образом:

>>> pair = re.compile(r".*(.).*\1")
>>> pair.match("717ak").group(1)
'7'

# Error because re.match() returns None, which doesn't have a group() method:
>>> pair.match("718ak").group(1)
Traceback (most recent call last):
  File "<pyshell#23>", line 1, in <module>
    re.match(r".*(.).*\1", "718ak").group(1)
AttributeError: 'NoneType' object has no attribute 'group'

>>> pair.match("354aa").group(1)
'a'

Моделирование scanf()

В настоящее время в Python нет эквивалента scanf(). Регулярные выражения обычно более мощные, хотя и более многословные, чем строки формата scanf(). В таблице ниже приведены некоторые более или менее эквивалентные отображения между токенами формата scanf() и регулярными выражениями.

scanf() Токен

Регулярное выражение

%c

.

%5c

.{5}

%d

[-+]?\d+

%e, %E, %f, %g

[-+]?(\d+(\.\d*)?|\.\d+)([eE][-+]?\d+)?

%i

[-+]?(0[xX][\dA-Fa-f]+|0[0-7]*|\d+)

%o

[-+]?[0-7]+

%s

\S+

%u

\d+

%x, %X

[-+]?(0[xX])?[\dA-Fa-f]+

Чтобы извлечь имя файла и цифры из строки типа

/usr/sbin/sendmail - 0 errors, 4 warnings

можно использовать формат scanf(), например

%s - %d errors, %d warnings

Эквивалентным регулярным выражением будет

(\S+) - (\d+) errors, (\d+) warnings

поиск() против совпадения()

Python предлагает две различные примитивные операции, основанные на регулярных выражениях: re.match() проверяет совпадение только в начале строки, а re.search() проверяет совпадение в любом месте строки (именно это делает Perl по умолчанию).

Например:

>>> re.match("c", "abcdef")    # No match
>>> re.search("c", "abcdef")   # Match
<re.Match object; span=(2, 3), match='c'>

Регулярные выражения, начинающиеся с '^', могут быть использованы с search() для ограничения совпадения в начале строки:

>>> re.match("c", "abcdef")    # No match
>>> re.search("^c", "abcdef")  # No match
>>> re.search("^a", "abcdef")  # Match
<re.Match object; span=(0, 1), match='a'>

Обратите внимание, что в режиме MULTILINE match() совпадает только начало строки, тогда как использование search() с регулярным выражением, начинающимся с '^', будет совпадать в начале каждой строки.

>>> re.match('X', 'A\nB\nX', re.MULTILINE)  # No match
>>> re.search('^X', 'A\nB\nX', re.MULTILINE)  # Match
<re.Match object; span=(4, 5), match='X'>

Создание телефонной книги

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

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

>>> text = """Ross McFluff: 834.345.1254 155 Elm Street
...
... Ronald Heathmore: 892.345.3428 436 Finley Avenue
... Frank Burger: 925.541.7625 662 South Dogwood Way
...
...
... Heather Albrecht: 548.326.4584 919 Park Place"""

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

>>> entries = re.split("\n+", text)
>>> entries
['Ross McFluff: 834.345.1254 155 Elm Street',
'Ronald Heathmore: 892.345.3428 436 Finley Avenue',
'Frank Burger: 925.541.7625 662 South Dogwood Way',
'Heather Albrecht: 548.326.4584 919 Park Place']

Наконец, разделите каждую запись на список с именем, фамилией, номером телефона и адресом. Мы используем параметр maxsplit в split(), потому что в адресе есть пробелы - наш шаблон разбиения:

>>> [re.split(":? ", entry, 3) for entry in entries]
[['Ross', 'McFluff', '834.345.1254', '155 Elm Street'],
['Ronald', 'Heathmore', '892.345.3428', '436 Finley Avenue'],
['Frank', 'Burger', '925.541.7625', '662 South Dogwood Way'],
['Heather', 'Albrecht', '548.326.4584', '919 Park Place']]

Шаблон :? соответствует двоеточию после фамилии, чтобы оно не встречалось в списке результатов. С помощью maxsplit из 4 мы могли бы отделить номер дома от названия улицы:

>>> [re.split(":? ", entry, 4) for entry in entries]
[['Ross', 'McFluff', '834.345.1254', '155', 'Elm Street'],
['Ronald', 'Heathmore', '892.345.3428', '436', 'Finley Avenue'],
['Frank', 'Burger', '925.541.7625', '662', 'South Dogwood Way'],
['Heather', 'Albrecht', '548.326.4584', '919', 'Park Place']]

Обработка текста

sub() заменяет каждое вхождение шаблона строкой или результатом функции. Этот пример демонстрирует использование sub() с функцией для «измельчения» текста, или случайного упорядочивания всех символов в каждом слове предложения, за исключением первого и последнего символов:

>>> def repl(m):
...     inner_word = list(m.group(2))
...     random.shuffle(inner_word)
...     return m.group(1) + "".join(inner_word) + m.group(3)
>>> text = "Professor Abdolmalek, please report your absences promptly."
>>> re.sub(r"(\w)(\w+)(\w)", repl, text)
'Poefsrosr Aealmlobdk, pslaee reorpt your abnseces plmrptoy.'
>>> re.sub(r"(\w)(\w+)(\w)", repl, text)
'Pofsroser Aodlambelk, plasee reoprt yuor asnebces potlmrpy.'

Поиск всех наречий

findall() соответствует все вхождениям шаблона, а не только первому, как search(). Например, если писатель хочет найти все наречия в тексте, он может использовать findall() следующим образом:

>>> text = "He was carefully disguised but captured quickly by police."
>>> re.findall(r"\w+ly\b", text)
['carefully', 'quickly']

Нахождение всех наречий и их позиций

Если требуется больше информации обо всех совпадениях шаблона, чем о совпадающем тексте, полезно использовать finditer(), так как он предоставляет match objects вместо строк. Продолжая предыдущий пример, если писатель хочет найти все наречия и их позиции в некотором тексте, он будет использовать finditer() следующим образом:

>>> text = "He was carefully disguised but captured quickly by police."
>>> for m in re.finditer(r"\w+ly\b", text):
...     print('%02d-%02d: %s' % (m.start(), m.end(), m.group(0)))
07-16: carefully
40-47: quickly

Строковая нотация

Строковая нотация (r"text") сохраняет регулярные выражения в нормальном состоянии. Без него каждый обратный слеш ('\') в регулярном выражении пришлось бы дополнять другим слешем, чтобы избежать его. Например, две следующие строки кода функционально идентичны:

>>> re.match(r"\W(.)\1\W", " ff ")
<re.Match object; span=(0, 4), match=' ff '>
>>> re.match("\\W(.)\\1\\W", " ff ")
<re.Match object; span=(0, 4), match=' ff '>

Если нужно сопоставить литеральную обратную косую черту, она должна быть экранирована в регулярном выражении. В необработанной строковой нотации это означает r"\\". Без необработанной строковой нотации необходимо использовать "\\\\", что делает следующие строки кода функционально идентичными:

>>> re.match(r"\\", r"\\")
<re.Match object; span=(0, 1), match='\\'>
>>> re.match("\\\\", r"\\")
<re.Match object; span=(0, 1), match='\\'>

Написание токенизатора

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

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

from typing import NamedTuple
import re

class Token(NamedTuple):
    type: str
    value: str
    line: int
    column: int

def tokenize(code):
    keywords = {'IF', 'THEN', 'ENDIF', 'FOR', 'NEXT', 'GOSUB', 'RETURN'}
    token_specification = [
        ('NUMBER',   r'\d+(\.\d*)?'),  # Integer or decimal number
        ('ASSIGN',   r':='),           # Assignment operator
        ('END',      r';'),            # Statement terminator
        ('ID',       r'[A-Za-z]+'),    # Identifiers
        ('OP',       r'[+\-*/]'),      # Arithmetic operators
        ('NEWLINE',  r'\n'),           # Line endings
        ('SKIP',     r'[ \t]+'),       # Skip over spaces and tabs
        ('MISMATCH', r'.'),            # Any other character
    ]
    tok_regex = '|'.join('(?P<%s>%s)' % pair for pair in token_specification)
    line_num = 1
    line_start = 0
    for mo in re.finditer(tok_regex, code):
        kind = mo.lastgroup
        value = mo.group()
        column = mo.start() - line_start
        if kind == 'NUMBER':
            value = float(value) if '.' in value else int(value)
        elif kind == 'ID' and value in keywords:
            kind = value
        elif kind == 'NEWLINE':
            line_start = mo.end()
            line_num += 1
            continue
        elif kind == 'SKIP':
            continue
        elif kind == 'MISMATCH':
            raise RuntimeError(f'{value!r} unexpected on line {line_num}')
        yield Token(kind, value, line_num, column)

statements = '''
    IF quantity THEN
        total := total + price * quantity;
        tax := price * 0.05;
    ENDIF;
'''

for token in tokenize(statements):
    print(token)

Токенизатор выдает следующий результат:

Token(type='IF', value='IF', line=2, column=4)
Token(type='ID', value='quantity', line=2, column=7)
Token(type='THEN', value='THEN', line=2, column=16)
Token(type='ID', value='total', line=3, column=8)
Token(type='ASSIGN', value=':=', line=3, column=14)
Token(type='ID', value='total', line=3, column=17)
Token(type='OP', value='+', line=3, column=23)
Token(type='ID', value='price', line=3, column=25)
Token(type='OP', value='*', line=3, column=31)
Token(type='ID', value='quantity', line=3, column=33)
Token(type='END', value=';', line=3, column=41)
Token(type='ID', value='tax', line=4, column=8)
Token(type='ASSIGN', value=':=', line=4, column=12)
Token(type='ID', value='price', line=4, column=15)
Token(type='OP', value='*', line=4, column=21)
Token(type='NUMBER', value=0.05, line=4, column=23)
Token(type='END', value=';', line=4, column=27)
Token(type='ENDIF', value='ENDIF', line=5, column=4)
Token(type='END', value=';', line=5, column=9)
Frie09

Фридл, Джеффри. Mastering Regular Expressions. 3-е изд., O’Reilly Media, 2009. В третьем издании книги больше не рассматривается Python, но в первом издании очень подробно рассматривается написание хороших шаблонов регулярных выражений.

Вернуться на верх