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

Введение

Регулярные выражения используются для сжатого описания некоторого множества строк с помощью шаблонов, без необходимости перечисления всех элементов этого множества или явного написания функции для проверки. Большинство символов соответствуют сами себе («a» соответствует «a» и т. д.). Исключения из этого правила называются метасимволами: .

Термин «Регулярные выражения» является переводом с английского словосочетания «Regular expressions». Перевод не очень точно отражает смысл, правильнее было бы «шаблонные выражения». Регулярное выражение, или коротко «регулярка», состоит из обычных символов и специальных командных последовательностей. Например, задаёт любую цифру, а — задает любую последовательность из одной или более цифр.

Работа с регулярками реализована во всех современных языках программирования. Однако существует несколько «диалектов», поэтому функционал регулярных выражений может различаться от языка к языку. В некоторых языках программирования регулярками пользоваться очень удобно (например, в питоне), в некоторых — не слишком (например, в C++).

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

Регулярка Её смысл
В точности текст «simple text»
Последовательности из 5 цифр означает любую цифру — ровно 5 раз
Даты в формате ДД/ММ/ГГГГ (и прочие куски, на них похожие, например, 98/76/5432)
«Слова» в точности из трёх букв/цифр означает границу слова (с одной стороны буква/цифра, а с другой — нет) — любая буква/цифра, — ровно три раза
Целое число, например, 7, +17, -42, 0013 (возможны ведущие нули) — либо -, либо +, либо пусто — последовательность из 1 или более цифр
Действительное число, возможно в экспоненциальной записиНапример, 0.2, +5.45, -.4, 6e23, -3.17E-14. См. ниже картинку.

Сила и ответственность

Регулярные выражения, или коротко, регулярки — это очень мощный инструмент. Но использовать их следует с умом и осторожностью, и только там, где они действительно приносят пользу, а не вред. Во-первых, плохо написанные регулярные выражения работают медленно. Во-вторых, их зачастую очень сложно читать, особенно если регулярка написана не лично тобой пять минут назад. В-третьих, очень часто даже небольшое изменение задачи (того, что требуется найти) приводит к значительному изменению выражения. Поэтому про регулярки часто говорят, что это write only code (код, который только пишут с нуля, но не читают и не правят). А также шутят: Некоторые люди, когда сталкиваются с проблемой, думают «Я знаю, я решу её с помощью регулярных выражений.» Теперь у них две проблемы.

re.match функция

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


Синтаксис функции:

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

Параметры функции:

параметры описание
шаблон Матч регулярное выражение
строка Строка для соответствия.
флаги Флаг, регулярное выражение соответствия используется для управления, например: соответствует ли чувствительны к регистру, многострочный, и так далее.

Успешный метод матча re.match возвращает объект соответствия, в противном случае None.

Мы можем использовать эту группу (NUM) или группы () функцию, чтобы получить объекты, соответствующие выражения совпадают.

Соответствующие методы объекта описание
группа (Num = 0) Весь соответствующий строковое выражение, группа () может ввести более одного номера группы, в этом случае он будет возвращать значение, соответствующее этим группам кортежей.
группы () Он возвращает кортеж из всех групп строки, от 1 до количества, содержащегося в группе.

Пример 1:

#!/usr/bin/python
# -*- coding: UTF-8 -*- 

import re
print(re.match('www', 'www.w3big.com').span())  # 在起始位置匹配
print(re.match('com', 'www.w3big.com'))         # 不在起始位置匹配

Запуск в приведенном выше примере выход:

(0, 3)
None

Пример 2:

#!/usr/bin/python3
import re

line = "Cats are smarter than dogs"

matchObj = re.match( r'(.*) are (.*?) .*', line, re.M|re.I)

if matchObj:
   print ("matchObj.group() : ", matchObj.group())
   print ("matchObj.group(1) : ", matchObj.group(1))
   print ("matchObj.group(2) : ", matchObj.group(2))
else:
   print ("No match!!")

Результаты приведенных выше примерах, являются следующими:

matchObj.group() :  Cats are smarter than dogs
matchObj.group(1) :  Cats
matchObj.group(2) :  smarter

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

Строка шаблона, используя специальный синтаксис для обозначения регулярное выражение:

Буквы и цифры сами. Регулярное выражение при букв и цифр совпадают ту же строку.

Большинство из букв и цифр будет иметь различное значение, когда ему предшествует обратный слэш.

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

Сам Backslash должен использовать побег символ обратной косой.

Поскольку регулярные выражения обычно содержат символы, так что вам лучше использовать исходную строку, чтобы представлять их. Элементы схемы (например, г ‘/ т’, что эквивалентно ‘// Т’) совпадает с соответствующим специальные символы.

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

режим описание
^ Соответствует началу строки
$ Соответствует концу строки.
, Соответствует любому символу, кроме символа новой строки, если указан флаг re.DOTALL, вы можете соответствовать любому символу, включая символ новой строки.
Он используется для представления группы символов, перечисленных отдельно: матч ‘а’, ‘т’ или ‘K’
Не [] символов: соответствует в дополнение к а, Ь, с символами.
Re * 0 или более выражениям.
Re + Один или более совпадающих выражений.
повторно? Матч 0 или 1 по предшествующих регулярных выражений для определения сегментов, не жадный путь
Re {п}
повторно {п,} Точное соответствие п предыдущего выражения.
Re {п, т} Матч п в т раз по предшествующих регулярных выражений для определения сегментов, жадный путь
а | б Совпадение или б
(Re) Выражение матч G в скобках, также представляет собой группу
(? Imx) Регулярное выражение состоит из трех дополнительных флагов: я, м, или х. Она влияет только на область в скобках.
(? -imx) Регулярные выражения Закрыть я, м, или х необязательный флаг. Она влияет только на область в скобках.
(?: Re) Аналогично (…), но не представляет собой группу,
(Imx 😕 Re) Я использую в круглые скобки, м или х необязательный флаг
(-imx 😕 Re) Не используйте I, M в круглых скобках, или х дополнительный флаг
(? # …) Примечание.
(? = Re) Форвард уверен разделитель. Если содержится регулярное выражение, представленное здесь …, успешно матчи в текущем местоположении, и не иначе. Тем не менее, как только содержала выражение была опробована, согласующий двигатель не продвигается, остальная часть узора даже попробовать разделителем правильно.
(?! Re) Нападающий отрицанием разделителем. И, конечно, противоречит разделителем, успешным, когда содержащийся выражение не совпадает с текущей позиции в строке
(?> Re) Независимый поиск по шаблону, устраняя откаты.
\ W Матч алфавитно-цифровой и нижнее подчеркивание
\ W Матч не буквенно-цифровых и подчеркивания
\ S Соответствует любой символ пробела, что эквивалентно .
\ S Соответствует любой непустой символ
\ D Соответствует любому количеству, которое эквивалентно .
\ D Соответствует любому нечисловая
\ A Соответствует началу строки
\ Z Матч конец строки, если она существует символ новой строки, только до конца строки, чтобы соответствовать новой строки. с
\ Z конец строки Match
\ G Матч Матч завершен последнюю позицию.
\ B Матчи границы слова, то есть, оно относится к месту и пробелы между словами. Например, ‘эр \ Ъ’ не может сравниться с «никогда» в «эр», но не может сравниться с «глаголом» в «эр».
\ B Матч граница слова. ‘Er \ B’ может соответствовать «глагол» в «эр», но не может сравниться с «никогда» в «эр».
\ N, \ т, и тому подобное. Соответствует новой строки. Соответствует символу табуляции. подождите
\ 1 … \ 9 Соответствующие подвыражения п-го пакета.
\ 10 Матч первые п пакетов подвыражению, если он после матча. В противном случае, выражение относится к восьмеричный код.

Regex Search and Match


Call re.search(regex, subject) to apply a regex pattern to a subject string. The function returns None if the matching attempt fails, and a Match object otherwise. Since None evaluates to False, you can easily use re.search() in an if statement. The Match object stores details about the part of the string matched by the regular expression pattern.

You can set regex matching modes by specifying a special constant as a third parameter to re.search(). re.I or re.IGNORECASE applies the pattern case insensitively. re.S or re.DOTALL makes the dot match newlines. re.M or re.MULTILINE makes the caret and dollar match after and before line breaks in the subject string. There is no difference between the single-letter and descriptive options, except for the number of characters you have to type in. To specify more than one option, “or” them together with the | operator: re.search("^a", "abc", re.I | re.M).

By default, Python’s regex engine only considers the letters A through Z, the digits 0 through 9, and the underscore as “word characters”. Specify the flag re.L or re.LOCALE to make \w match all characters that are considered letters given the current locale settings. Alternatively, you can specify re.U or re.UNICODE to treat all letters from all scripts as word characters. The setting also affects word boundaries.

Do not confuse re.search() with re.match(). Both functions do exactly the same, with the important distinction that re.search() will attempt the pattern throughout the string, until it finds a match. re.match() on the other hand, only attempts the pattern at the very start of the string. Basically, re.match("regex", subject) is the same as re.search("\Aregex", subject). Note that re.match() does not require the regex to match the entire string. re.match("a", "ab") will succeed.

Python 3.4 adds a new re.fullmatch() function. This function only returns a Match object if the regex matches the string entirely. Otherwise it returns None. re.fullmatch("regex", subject) is the same as re.search("\Aregex\Z", subject). This is useful for validating user input. If subject is an empty string then fullmatch() evaluates to True for any regex that can find a zero-length match.

To get all matches from a string, call re.findall(regex, subject). This will return an array of all non-overlapping regex matches in the string. “Non-overlapping” means that the string is searched through from left to right, and the next match attempt starts beyond the previous match. If the regex contains one or more capturing groups, re.findall() returns an array of tuples, with each tuple containing text matched by all the capturing groups. The overall regex match is not included in the tuple, unless you place the entire regex inside a capturing group.

More efficient than re.findall() is re.finditer(regex, subject). It returns an iterator that enables you to loop over the regex matches in the subject string: for m in re.finditer(regex, subject). The for-loop variable m is a Match object with the details of the current match.

Unlike re.search() and re.match(), re.findall() and re.finditer() do not support an optional third parameter with regex matching flags. Instead, you can use global mode modifiers at the start of the regex. E.g. “(?i)regex” matches regex case insensitively.

Группирующие скобки (…) и match-объекты в питоне

Match-объекты

Если функции , не находят соответствие шаблону в строке, то они возвращают , функция возващает пустой итератор. Однако если соответствие найдено, то возвращается -объект. Эта штука содержит в себе кучу полезной информации о соответствии шаблону. В отличие от предыдущих функций, возвращает «простой и понятный» список соответствий. Полный набор атрибутов -объекта можно посмотреть в , а здесь приведём самое полезное.

Метод Описание Пример
Подстрока, соответствующая всему шаблону
Индекс в исходной строке, начиная с которого идёт найденная подстрока
Индекс в исходной строке, который следует сразу за найденной подстрока

Группирующие скобки

Если в шаблоне регулярного выражения встречаются скобки без , то они становятся группирующими. В match-объекте, который возвращают , и , по каждой такой группе можно получить ту же информацию, что и по всему шаблону. А именно часть подстроки, которая соответствует , а также индексы начала и окончания в исходной строке. Достаточно часто это бывает полезно.

import re
pattern = r'\s*(+)(\d+)\s*'
string = r'---   Опять45   ---'
match = re.search(pattern, string)
print(f'Найдена подстрока >{match.group(0)}< с позиции {match.start(0)} до {match.end(0)}')
print(f'Группа букв >{match.group(1)}< с позиции {match.start(1)} до {match.end(1)}')
print(f'Группа цифр >{match.group(2)}< с позиции {match.start(2)} до {match.end(2)}')
###
# Найдена подстрока >   Опять45   < с позиции 3 до 16
# Группа букв >Опять< с позиции 6 до 11
# Группа цифр >45< с позиции 11 до 13

Тонкости со скобками и нумерацией групп.

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

Найдена подстрока >   Опять45   < с позиции 3 до 16
Группа букв >ь< с позиции 10 до 11
Группа цифр >5< с позиции 12 до 13

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

import re
pattern = r'((\d)(\d))((\d)(\d))'
string = r'123456789'
match = re.search(pattern, string)
print(f'Найдена подстрока >{match.group(0)}< с позиции {match.start(0)} до {match.end(0)}')
for i in range(1, match.groups()+1):
    print(f'Группа №{i} >{match.group(i)}< с позиции {match.start(i)} до {match.end(i)}')
###
Найдена подстрока >1234< с позиции 0 до 4
Группа №1 >12< с позиции 0 до 2
Группа №2 >1< с позиции 0 до 1
Группа №3 >2< с позиции 1 до 2
Группа №4 >34< с позиции 2 до 4
Группа №5 >3< с позиции 2 до 3
Группа №6 >4< с позиции 3 до 4

Группы и

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

import re
print(re.findall(r'(+)(\d*)', r'foo3, im12, go, 24buz42'))
# -> 

Группы и

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

import re
print(re.split(r'(\s*)([+*/-])(\s*)', r'12  +  13*15   - 6'))
# -> 

The search Function

This function searches for first occurrence of RE pattern within string with optional flags.

Here is the syntax for this function −

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

Here is the description of the parameters −

Sr.No. Parameter & Description
1

pattern

This is the regular expression to be matched.

2

string

This is the string, which would be searched to match the pattern anywhere in the string.

3

flags

You can specify different flags using bitwise OR (|). These are modifiers, which are listed in the table below.

The re.search function returns a match object on success, none on failure. We use group(num) or groups() function of match object to get matched expression.

Sr.No. Match Object Methods & Description
1

group(num=0)

This method returns entire match (or specific subgroup num)

2

groups()

This method returns all matching subgroups in a tuple (empty if there weren’t any)

Example

#!/usr/bin/python
import re

line = "Cats are smarter than dogs";

searchObj = re.search( r'(.*) are (.*?) .*', line, re.M|re.I)

if searchObj:
   print "searchObj.group() : ", searchObj.group()
   print "searchObj.group(1) : ", searchObj.group(1)
   print "searchObj.group(2) : ", searchObj.group(2)
else:
   print "Nothing found!!"

When the above code is executed, it produces following result −

searchObj.group() :  Cats are smarter than dogs
searchObj.group(1) :  Cats
searchObj.group(2) :  smarter

Пример использования всех основных функций

import re

match = re.search(r'\d\d\D\d\d', r'Телефон 123-12-12')
print(match.group(0) if match else 'Not found')
# -> 23-12
match = re.search(r'\d\d\D\d\d', r'Телефон 1231212')
print(match.group(0) if match else 'Not found')
# -> Not found

match = re.fullmatch(r'\d\d\D\d\d', r'12-12')
print('YES' if match else 'NO')
# -> YES
match = re.fullmatch(r'\d\d\D\d\d', r'Т. 12-12')
print('YES' if match else 'NO')
# -> NO

print(re.split(r'\W+', 'Где, скажите мне, мои очки??!'))
# -> 

print(re.findall(r'\d\d\.\d\d\.\d{4}',
                 r'Эта строка написана 19.01.2018, а могла бы и 01.09.2017'))
# -> 

for m in re.finditer(r'\d\d\.\d\d\.\d{4}', r'Эта строка написана 19.01.2018, а могла бы и 01.09.2017'):
    print('Дата', m.group(0), 'начинается с позиции', m.start())
# -> Дата 19.01.2018 начинается с позиции 20
# -> Дата 01.09.2017 начинается с позиции 45

print(re.sub(r'\d\d\.\d\d\.\d{4}',
             r'DD.MM.YYYY',
             r'Эта строка написана 19.01.2018, а могла бы и 01.09.2017'))
# -> Эта строка написана DD.MM.YYYY, а могла бы и DD.MM.YYYY

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

Так как символ в питоновских строках также необходимо экранировать, то в результате в шаблонах могут возникать конструкции вида . Первый слеш означает, что следующий за ним символ нужно оставить «как есть». Третий также. В результате с точки зрения питона означает просто два слеша . Теперь с точки зрения движка регулярных выражений, первый слеш экранирует второй. Тем самым как шаблон для регулярки означает просто текст . Для того, чтобы не было таких нагромождений слешей, перед открывающей кавычкой нужно поставить символ , что скажет питону «не рассматривай \ как экранирующий символ (кроме случаев экранирования открывающей кавычки)». Соответственно можно будет писать .

Использование дополнительных флагов в питоне

Константа Её смысл
По умолчанию , , , , , , , соответствуют все юникодные символы с соответствующим качеством. Например, соответствуют не только арабские цифры, но и вот такие: ٠١٢٣٤٥٦٧٨٩. ускоряет работу, если все соответствия лежат внутри ASCII.
Не различать заглавные и маленькие буквы. Работает медленнее, но иногда удобно
Специальные символы и соответствуют началу и концу каждой строки
По умолчанию символ конца строки не подходит под точку. С этим флагом точка — вообще любой символ

The match Function

This function attempts to match RE pattern to string with optional flags.

Here is the syntax for this function −

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

Here is the description of the parameters −

Sr.No. Parameter & Description
1

pattern

This is the regular expression to be matched.

2

string

This is the string, which would be searched to match the pattern at the beginning of string.

3

flags


You can specify different flags using bitwise OR (|). These are modifiers, which are listed in the table below.

The re.match function returns a match object on success, None on failure. We usegroup(num) or groups() function of match object to get matched expression.

Sr.No. Match Object Method & Description
1

group(num = 0)

This method returns entire match (or specific subgroup num)

2

groups()

This method returns all matching subgroups in a tuple (empty if there weren’t any)

Example

#!/usr/bin/python3
import re

line = "Cats are smarter than dogs"

matchObj = re.match( r'(.*) are (.*?) .*', line, re.M|re.I)

if matchObj:
   print ("matchObj.group() : ", matchObj.group())
   print ("matchObj.group(1) : ", matchObj.group(1))
   print ("matchObj.group(2) : ", matchObj.group(2))
else:
   print ("No match!!")

When the above code is executed, it produces the following result −

matchObj.group() :  Cats are smarter than dogs
matchObj.group(1) :  Cats
matchObj.group(2) :  smarter

Special-casing comprehensions

Another common use-case is comprehensions (list/set/dict, and genexps). As above, proposals have been made for comprehension-specific solutions.

  1. where, let, or given:

    stuff = [(y, x/y) where y = f(x) for x in range(5)]
    stuff = [(y, x/y) let y = f(x) for x in range(5)]
    stuff = [(y, x/y) given y = f(x) for x in range(5)]
    

    This brings the subexpression to a location in between the ‘for’ loop and the expression. It introduces an additional language keyword, which creates conflicts. Of the three, where reads the most cleanly, but also has the greatest potential for conflict (e.g. SQLAlchemy and numpy have where methods, as does tkinter.dnd.Icon in the standard library).

  2. with NAME = EXPR:

    stuff = [(y, x/y) with y = f(x) for x in range(5)]
    

    As above, but reusing the with keyword. Doesn’t read too badly, and needs no additional language keyword. Is restricted to comprehensions, though, and cannot as easily be transformed into «longhand» for-loop syntax. Has the C problem that an equals sign in an expression can now create a name binding, rather than performing a comparison. Would raise the question of why «with NAME = EXPR:» cannot be used as a statement on its own.

  3. with EXPR as NAME:

    stuff = [(y, x/y) with f(x) as y for x in range(5)]
    

    As per option 2, but using as rather than an equals sign. Aligns syntactically with other uses of as for name binding, but a simple transformation to for-loop longhand would create drastically different semantics; the meaning of with inside a comprehension would be completely different from the meaning as a stand-alone statement, while retaining identical syntax.

Capturing condition values

Assignment expressions can be used to good effect in the header of an if or while statement:

# Loop-and-a-half
while (command := input("> ")) != "quit":
    print("You entered:", command)

# Capturing regular expression match objects
# See, for instance, Lib/pydoc.py, which uses a multiline spelling
# of this effect
if match := re.search(pat, text):
    print("Found:", match.group(0))
# The same syntax chains nicely into 'elif' statements, unlike the
# equivalent using assignment statements.
elif match := re.search(otherpat, text):
    print("Alternate found:", match.group(0))
elif match := re.search(third, text):
    print("Fallback found:", match.group(0))

# Reading socket data until an empty string is returned
while data := sock.recv(8192):
    print("Received data:", data)

Connection methods

Close the connection now (rather than whenever .__del__() is called).

The connection will be unusable from this point forward; an (or subclass) exception will be raised if any operation is attempted with the connection. The same applies to all cursor objects trying to use the connection. Note that closing a connection without committing the changes first will cause an implicit rollback to be performed.

()

Commit any pending transaction to the database.

Note that if the database supports an auto-commit feature, this must be initially off. An interface method may be provided to turn it back on.

Database modules that do not support transactions should implement this method with void functionality.

()

This method is optional since not all databases provide transaction support.

In case a database does provide transactions this method causes the database to roll back to the start of any pending transaction. Closing a connection without committing the changes first will cause an implicit rollback to be performed.

Поиск и замена

Модуль повторного Python предоставляет re.sub на матч замены строки.

Синтаксис:

re.sub(pattern, repl, string, max=0)

Возвращаемая строка это строка с крайней левой RE матчей не будет повторяться, чтобы заменить. Если шаблон не найден, то символы будут возвращены без изменений.

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

Пример:

#!/usr/bin/python
import re

phone = "2004-959-559 # This is Phone Number"

# Delete Python-style comments
num = re.sub(r'#.*$', "", phone)
print "Phone Num : ", num

# Remove anything other than digits
num = re.sub(r'\D', "", phone)    
print "Phone Num : ", num
Phone Num :  2004-959-559
Phone Num :  2004959559

Public and Internal Interfaces

Any backwards compatibility guarantees apply only to public interfaces. Accordingly, it is important that users be able to clearly distinguish between public and internal interfaces.

Documented interfaces are considered public, unless the documentation explicitly declares them to be provisional or internal interfaces exempt from the usual backwards compatibility guarantees. All undocumented interfaces should be assumed to be internal.

To better support introspection, modules should explicitly declare the names in their public API using the __all__ attribute. Setting __all__ to an empty list indicates that the module has no public API.

Even with __all__ set appropriately, internal interfaces (packages, modules, classes, functions, attributes or other names) should still be prefixed with a single leading underscore.

An interface is also considered internal if any containing namespace (package, module or class) is considered internal.


С этим читают