Как использовать в Python модуль регулярных выражений re (match, search, sub и т.д.)

Бизнес

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

В этом разделе мы сначала объясним функции и методы модуля re.

  • Составление шаблонов регулярных выражений:compile()
  • объект соответствия
  • Проверьте, совпадает ли начало строки, извлеките:match()
  • Проверьте наличие совпадений, не ограничивающихся началом:search()
  • Проверьте, совпадает ли вся строка:fullmatch()
  • Получите список всех подходящих деталей:findall()
  • Получить все совпадающие части в виде итератора:finditer()
  • Замените соответствующую деталь:sub(),subn()
  • Разделение строк с помощью шаблонов регулярных выражений:split()

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

  • Метасимволы регулярных выражений, специальные последовательности и предостережения в Python
  • Установка флага
    • Ограничивается символами ASCII:re.ASCII
    • Не чувствителен к регистру:re.IGNORECASE
    • Сопоставьте начало и конец каждой строки:re.MULTILINE
    • Укажите несколько флагов
  • Жадные и нежадные матчи

Составьте шаблон регулярного выражения: compile()

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

Работайте с функцией

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

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

import re

s = 'aaa@xxx.com, bbb@yyy.com, ccc@zzz.net'

m = re.match(r'([a-z]+)@([a-z]+)\.com', s)
print(m)
# <re.Match object; span=(0, 11), match='aaa@xxx.com'>

result = re.sub(r'([a-z]+)@([a-z]+)\.com', 'new-address', s)
print(result)
# new-address, new-address, ccc@zzz.net

Обратите внимание, что [a-z] в шаблоне регулярного выражения в данном примере означает любой символ от a до z (т.е. строчный алфавит), а + означает повторение предыдущего шаблона (в данном случае [a-z]) один или более раз. [a-z]+ соответствует любой строке, в которой повторяется один или несколько символов нижнего регистра алфавита.

. является метасимволом (символом со специальным значением) и должен быть экранирован обратной косой чертой.

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

Выполняется в методе объекта шаблона регулярного выражения

Вторым способом обработки регулярных выражений в модуле re является метод объекта шаблона регулярного выражения.

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

p = re.compile(r'([a-z]+)@([a-z]+)\.com')

print(p)
# re.compile('([a-z]+)@([a-z]+)\\.com')

print(type(p))
# <class 're.Pattern'>

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

m = p.match(s)
print(m)
# <re.Match object; span=(0, 11), match='aaa@xxx.com'>

result = p.sub('new-address', s)
print(result)
# new-address, new-address, ccc@zzz.net

Все функции re.xxx(), описанные ниже, также предоставляются как методы объекта регулярного выражения.

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

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

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

match(), search() и т.д. возвращают объект match.

s = 'aaa@xxx.com'

m = re.match(r'[a-z]+@[a-z]+\.[a-z]+', s)
print(m)
# <re.Match object; span=(0, 11), match='aaa@xxx.com'>

print(type(m))
# <class 're.Match'>

Соответствующая строка и позиция получаются с помощью следующих методов объекта match.

  • Получите местоположение матча:start(),end(),span()
  • Получить совпадающую строку:group()
  • Получите строку для каждой группы:groups()
print(m.start())
# 0

print(m.end())
# 11

print(m.span())
# (0, 11)

print(m.group())
# aaa@xxx.com

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

m = re.match(r'([a-z]+)@([a-z]+)\.([a-z]+)', s)
print(m)
# <re.Match object; span=(0, 11), match='aaa@xxx.com'>

print(m.groups())
# ('aaa', 'xxx', 'com')

Проверьте, совпадает ли начало строки, извлеките: match()

match() возвращает объект match, если начало строки соответствует шаблону.

Как упоминалось выше, объект match можно использовать для извлечения совпавшей подстроки или просто для проверки совпадения.

match() проверяет только начало. Если в начале нет совпадающей строки, возвращается None.

s = 'aaa@xxx.com, bbb@yyy.com, ccc@zzz.net'

m = re.match(r'[a-z]+@[a-z]+\.com', s)
print(m)
# <re.Match object; span=(0, 11), match='aaa@xxx.com'>

m = re.match(r'[a-z]+@[a-z]+\.net', s)
print(m)
# None

Проверка совпадений, не ограниченных началом, извлечение: search()

Как и функция match(), она возвращает объект match в случае совпадения.

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

s = 'aaa@xxx.com, bbb@yyy.com, ccc@zzz.net'

m = re.search(r'[a-z]+@[a-z]+\.net', s)
print(m)
# <re.Match object; span=(26, 37), match='ccc@zzz.net'>

m = re.search(r'[a-z]+@[a-z]+\.com', s)
print(m)
# <re.Match object; span=(0, 11), match='aaa@xxx.com'>

Если вы хотите получить все совпадающие части, используйте findall() или finditer(), как описано ниже.

Проверьте, совпадает ли вся строка: fullmatch()

Чтобы проверить, соответствует ли вся строка шаблону регулярного выражения, используйте fullmatch(). Это полезно, например, для проверки того, является ли строка действительной в качестве адреса электронной почты или нет.

Если вся строка совпадает, возвращается объект match.

s = 'aaa@xxx.com'

m = re.fullmatch(r'[a-z]+@[a-z]+\.com', s)
print(m)
# <re.Match object; span=(0, 11), match='aaa@xxx.com'>

Если есть несовпадающие части (только частичные совпадения или вообще нет совпадений), возвращается None.

s = '!!!aaa@xxx.com!!!'

m = re.fullmatch(r'[a-z]+@[a-z]+\.com', s)
print(m)
# None

Функция fullmatch() была добавлена в Python 3.4. Если вы хотите сделать то же самое в более ранних версиях, используйте функцию match() и совпадающий метасимвол $ в конце. Если вся строка от начала до конца не совпадает, возвращается None.

s = '!!!aaa@xxx.com!!!'

m = re.match(r'[a-z]+@[a-z]+\.com$', s)
print(m)
# None

Получение списка всех совпадающих частей: findall()

findall() возвращает список всех совпадающих подстрок. Обратите внимание, что элементами списка являются не объекты соответствия, а строки.

s = 'aaa@xxx.com, bbb@yyy.com, ccc@zzz.net'

result = re.findall(r'[a-z]+@[a-z]+\.[a-z]+', s)
print(result)
# ['aaa@xxx.com', 'bbb@yyy.com', 'ccc@zzz.net']

Количество совпавших частей можно проверить с помощью встроенной функции len(), которая возвращает количество элементов в списке.

print(len(result))
# 3

Группировка с круглыми скобками() в шаблоне регулярного выражения возвращает список кортежей, элементами которого являются строки каждой группы. Это эквивалентно groups() в объекте match.

result = re.findall(r'([a-z]+)@([a-z]+)\.([a-z]+)', s)
print(result)
# [('aaa', 'xxx', 'com'), ('bbb', 'yyy', 'com'), ('ccc', 'zzz', 'net')]

Групповые скобки () могут быть вложенными, поэтому если вы хотите получить полное совпадение, просто заключите все совпадение в круглые скобки ().

result = re.findall(r'(([a-z]+)@([a-z]+)\.([a-z]+))', s)
print(result)
# [('aaa@xxx.com', 'aaa', 'xxx', 'com'), ('bbb@yyy.com', 'bbb', 'yyy', 'com'), ('ccc@zzz.net', 'ccc', 'zzz', 'net')]

Если совпадение не найдено, возвращается пустой кортеж.

result = re.findall('[0-9]+', s)
print(result)
# []

Получение всех совпадающих частей в виде итератора: finditer()

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

Сам итератор нельзя распечатать с помощью print(), чтобы получить его содержимое. Если вы используете встроенную функцию next() или оператор for, вы можете получать содержимое по одному.

s = 'aaa@xxx.com, bbb@yyy.com, ccc@zzz.net'

result = re.finditer(r'[a-z]+@[a-z]+\.[a-z]+', s)
print(result)
# <callable_iterator object at 0x10b0efa90>

print(type(result))
# <class 'callable_iterator'>

for m in result:
    print(m)
# <re.Match object; span=(0, 11), match='aaa@xxx.com'>
# <re.Match object; span=(13, 24), match='bbb@yyy.com'>
# <re.Match object; span=(26, 37), match='ccc@zzz.net'>

Его также можно преобразовать в список с помощью функции list().

l = list(re.finditer(r'[a-z]+@[a-z]+\.[a-z]+', s))
print(l)
# [<re.Match object; span=(0, 11), match='aaa@xxx.com'>, <re.Match object; span=(13, 24), match='bbb@yyy.com'>, <re.Match object; span=(26, 37), match='ccc@zzz.net'>]

print(l[0])
# <re.Match object; span=(0, 11), match='aaa@xxx.com'>

print(type(l[0]))
# <class 're.Match'>

print(l[0].span())
# (0, 11)

Если вы хотите получить позицию всех совпадающих частей, нотация list comprehension удобнее, чем list().

print([m.span() for m in re.finditer(r'[a-z]+@[a-z]+\.[a-z]+', s)])
# [(0, 11), (13, 24), (26, 37)]

Итератор извлекает элементы по порядку. Обратите внимание, что если вы попытаетесь извлечь еще элементы после достижения конца, вы останетесь ни с чем.

result = re.finditer(r'[a-z]+@[a-z]+\.[a-z]+', s)

for m in result:
    print(m)
# <re.Match object; span=(0, 11), match='aaa@xxx.com'>
# <re.Match object; span=(13, 24), match='bbb@yyy.com'>
# <re.Match object; span=(26, 37), match='ccc@zzz.net'>

print(list(result))
# []

Замените подходящие части: sub(), subn()

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

s = 'aaa@xxx.com, bbb@yyy.com, ccc@zzz.net'

result = re.sub(r'[a-z]+@[a-z]+\.com', 'new-address', s)
print(result)
# new-address, new-address, ccc@zzz.net

print(type(result))
# <class 'str'>

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

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

\1Первая скобка
\2Вторая скобка
\3Третья скобка
result = re.sub(r'([a-z]+)@([a-z]+)\.com', r'\1@\2.net', s)
print(result)
# aaa@xxx.net, bbb@yyy.net, ccc@zzz.net

?P<xxx>
Если вы дадите группе имя, написав его в начале круглых скобок шаблона регулярного выражения, вы можете указать ее, используя имя вместо номера, как показано ниже.
\g<xxx>

result = re.sub(r'(?P<local>[a-z]+)@(?P<SLD>[a-z]+)\.com', r'\g<local>@\g<SLD>.net', s)
print(result)
# aaa@xxx.net, bbb@yyy.net, ccc@zzz.net

Аргумент count задает максимальное количество замен. Будет заменен только счетчик с левой стороны.

result = re.sub(r'[a-z]+@[a-z]+\.com', 'new-address', s, count=1)
print(result)
# new-address, bbb@yyy.com, ccc@zzz.net

subn() возвращает кортеж из замененной строки (такой же, как возвращаемое значение sub()) и количества замененных частей (количество совпавших с шаблоном).

result = re.subn(r'[a-z]+@[a-z]+\.com', 'new-address', s)
print(result)
# ('new-address, new-address, ccc@zzz.net', 2)

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

result = re.subn(r'(?P<local>[a-z]+)@(?P<SLD>[a-z]+)\.com', r'\g<local>@\g<SLD>.net', s)
print(result)
# ('aaa@xxx.net, bbb@yyy.net, ccc@zzz.net', 2)

result = re.subn(r'[a-z]+@[a-z]+\.com', 'new-address', s, count=1)
print(result)
# ('new-address, bbb@yyy.com, ccc@zzz.net', 1)

Разделение строк с помощью шаблонов регулярных выражений: split()

split() разделяет строку на части, соответствующие шаблону, и возвращает ее в виде списка.

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

s = '111aaa222bbb333'

result = re.split('[a-z]+', s)
print(result)
# ['111', '222', '333']

result = re.split('[0-9]+', s)
print(result)
# ['', 'aaa', 'bbb', '']

Аргумент maxsplit задает максимальное количество разбиений (частей). Будет разделен только граф с левой стороны.

result = re.split('[a-z]+', s, 1)
print(result)
# ['111', '222bbb333']

Метасимволы регулярных выражений, специальные последовательности и предостережения в Python

Основные метасимволы регулярных выражений (специальные символы) и специальные последовательности, которые можно использовать в модуле Python 3 re, следующие

метахарактерсодержание
.Любой отдельный символ, кроме новой строки (включая новую строку с флагом DOTALL)
^Начало строки (также совпадает с началом каждой строки с флагом MULTILINE)
$Конец строки (также совпадает с концом каждой строки с флагом MULTILINE)
*Повторите предыдущий узор более 0 раз
+Повторите предыдущий узор хотя бы один раз.
?Повторите предыдущий шаблон 0 или 1 раз
{m}Повторите предыдущий узор m раз
{m, n}Последний шаблон.m~nповторить
[]Набор символов[]Соответствует любому из этих символов
|ИЛИA|BСовпадает с образцом A или B
специальная последовательностьсодержание
\dДесятичные числа Unicode (ограничены числами ASCII с помощью флага ASCII)
\D\dОзначает противоположное этому.
\sПробельные символы Unicode (ограничены пробельными символами ASCII с помощью флага ASCII)
\S\sОзначает противоположное этому.
\wСловесные символы Unicode и знаки подчеркивания (ограничены буквенно-цифровыми символами ASCII и знаками подчеркивания флагом ASCII)
\W\wОзначает противоположное этому.

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

Также обратите внимание, что некоторые значения отличаются в Python 2.

Установка флага

Как показано в таблице выше, некоторые метасимволы и специальные последовательности меняют свой режим в зависимости от флага.

Здесь рассматриваются только основные флаги. Остальное смотрите в официальной документации.

Ограничено символами ASCII: re.ASCII

\wПо умолчанию для строк Python 3 оно также будет соответствовать двухбайтовым кандзи, буквенно-цифровым символам и т.д. Он не эквивалентен следующему, поскольку не является стандартным регулярным выражением.[a-zA-Z0-9_]

m = re.match(r'\w+', '漢字ABC123')
print(m)
# <re.Match object; span=(0, 11), match='漢字ABC123'>

m = re.match('[a-zA-Z0-9_]+', '漢字ABC123')
print(m)
# None

Если вы укажете re.ASCII для флагов аргументов в каждой функции или добавите следующий флаг inline в начало строки шаблона регулярного выражения, оно будет соответствовать только символам ASCII (оно не будет соответствовать двухбайтовым японским, буквенно-цифровым символам и т.д.).
(?a)
В этом случае следующие два эквивалентны.
\w=[a-zA-Z0-9_]

m = re.match(r'\w+', '漢字ABC123', flags=re.ASCII)
print(m)
# None

m = re.match(r'(?a)\w+', '漢字ABC123')
print(m)
# None

То же самое относится к компиляции с помощью re.compile(). Используйте аргумент flags или встроенные флаги.

p = re.compile(r'\w+', flags=re.ASCII)
print(p)
# re.compile('\\w+', re.ASCII)

print(p.match('漢字ABC123'))
# None

p = re.compile(r'(?a)\w+')
print(p)
# re.compile('(?a)\\w+', re.ASCII)

print(p.match('漢字ABC123'))
# None

ASCII также доступен в виде краткой формы re. A. Вы можете использовать либо.

print(re.ASCII is re.A)
# True

На \W, противоположное \W, также влияют флаги re.ASCII и inline.

m = re.match(r'\W+', '漢字ABC123')
print(m)
# None

m = re.match(r'\W+', '漢字ABC123', flags=re.ASCII)
print(m)
# <re.Match object; span=(0, 11), match='漢字ABC123'>

Как и в случае \w, следующие два по умолчанию соответствуют как однобайтовым, так и двухбайтовым символам, но ограничиваются однобайтовыми символами, если указаны флаги re.ASCII или inline.

  • Сопоставьте числа\d
  • Совпадает с пустым местом\s
  • Совмещает нечисловые данные\D
  • Совпадает с любым не пробелом.\S
m = re.match(r'\d+', '123')
print(m)
# <re.Match object; span=(0, 3), match='123'>

m = re.match(r'\d+', '123')
print(m)
# <re.Match object; span=(0, 3), match='123'>

m = re.match(r'\d+', '123', flags=re.ASCII)
print(m)
# <re.Match object; span=(0, 3), match='123'>

m = re.match(r'\d+', '123', flags=re.ASCII)
print(m)
# None

m = re.match(r'\s+', ' ')  # full-width space
print(m)
# <re.Match object; span=(0, 1), match='\u3000'>

m = re.match(r'\s+', ' ', flags=re.ASCII)
print(m)
# None

Не чувствителен к регистру:re.IGNORECASE

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

re.IGNORECASEЕсли этот флаг указан, то совпадение будет происходить без учета регистра. Эквивалентно флагу i в стандартных регулярных выражениях.

m = re.match('[a-zA-Z]+', 'abcABC')
print(m)
# <re.Match object; span=(0, 6), match='abcABC'>

m = re.match('[a-z]+', 'abcABC', flags=re.IGNORECASE)
print(m)
# <re.Match object; span=(0, 6), match='abcABC'>

m = re.match('[A-Z]+', 'abcABC', flags=re.IGNORECASE)
print(m)
# <re.Match object; span=(0, 6), match='abcABC'>

Вы можете использовать less than или equal to.

  • флаг инлайн(?i)
  • аббревиатураre.I

Сопоставьте начало и конец каждой строки:re.MULTILINE

^Мета-символы в этом регулярном выражении соответствуют началу строки.

По умолчанию сопоставляется только начало всей строки, но в следующем случае будет сопоставляться и начало каждой строки. Эквивалентно флагу m в стандартных регулярных выражениях.
re.MULTILINE

s = '''aaa-xxx
bbb-yyy
ccc-zzz'''

print(s)
# aaa-xxx
# bbb-yyy
# ccc-zzz

result = re.findall('[a-z]+', s)
print(result)
# ['aaa', 'xxx', 'bbb', 'yyy', 'ccc', 'zzz']

result = re.findall('^[a-z]+', s)
print(result)
# ['aaa']

result = re.findall('^[a-z]+', s, flags=re.MULTILINE)
print(result)
# ['aaa', 'bbb', 'ccc']

$Сопоставляет конец строки. По умолчанию сопоставляется только конец всей строки.
re.MULTILINEЕсли вы укажете это, он также будет соответствовать концу каждой строки.

result = re.findall('[a-z]+$', s)
print(result)
# ['zzz']

result = re.findall('[a-z]+$', s, flags=re.MULTILINE)
print(result)
# ['xxx', 'yyy', 'zzz']

Вы можете использовать less than или equal to.

  • флаг инлайн(?m)
  • аббревиатураre.M

Укажите несколько флагов

|Если вы хотите включить несколько флагов одновременно, используйте это. В случае с инлайн-флагами за каждым символом должна следовать буква, как показано ниже.
(?am)

s = '''aaa-xxx
漢漢漢-字字字
bbb-zzz'''

print(s)
# aaa-xxx
# 漢漢漢-字字字
# bbb-zzz

result = re.findall(r'^\w+', s, flags=re.M)
print(result)
# ['aaa', '漢漢漢', 'bbb']

result = re.findall(r'^\w+', s, flags=re.M | re.A)
print(result)
# ['aaa', 'bbb']

result = re.findall(r'(?am)^\w+', s)
print(result)
# ['aaa', 'bbb']

Жадные и нежадные матчи

Это общая проблема с регулярными выражениями, а не только проблема Python, но я напишу о ней, потому что она обычно доставляет мне неприятности.

По умолчанию используется жадное соответствие, которое соответствует самой длинной возможной строке.

  • *
  • +
  • ?
s = 'aaa@xxx.com, bbb@yyy.com'

m = re.match(r'.+com', s)
print(m)
# <re.Match object; span=(0, 24), match='aaa@xxx.com, bbb@yyy.com'>

print(m.group())
# aaa@xxx.com, bbb@yyy.com

Символ ? после него приводит к не жадному, минимальному совпадению, совпадающему с самой короткой возможной строкой.

  • *?
  • +?
  • ??
m = re.match(r'.+?com', s)
print(m)
# <re.Match object; span=(0, 11), match='aaa@xxx.com'>

print(m.group())
# aaa@xxx.com

Обратите внимание, что жадное соответствие по умолчанию может совпасть с неожиданными строками.

Copied title and URL