Для выполнения обработки регулярных выражений в Python мы используем модуль re из стандартной библиотеки. Он позволяет извлекать, заменять и разделять строки с помощью шаблонов регулярных выражений.
- re — Regular expression operations — Python 3.10.0 Documentation
- Regular Expression HOWTO — Python 3.10.0 Documentation
В этом разделе мы сначала объясним функции и методы модуля re.
- Составление шаблонов регулярных выражений:
compile()
- объект соответствия
- Проверьте, совпадает ли начало строки, извлеките:
match()
- Проверьте наличие совпадений, не ограничивающихся началом:
search()
- Проверьте, совпадает ли вся строка:
fullmatch()
- Получите список всех подходящих деталей:
findall()
- Получить все совпадающие части в виде итератора:
finditer()
- Замените соответствующую деталь:
sub()
,subn()
- Разделение строк с помощью шаблонов регулярных выражений:
split()
После этого я объясню метасимволы (специальные символы) и специальные последовательности регулярных выражений, которые можно использовать в модуле re. В принципе, это стандартный синтаксис регулярных выражений, но будьте осторожны с установкой флагов (особенно re.ASCII).
- Метасимволы регулярных выражений, специальные последовательности и предостережения в Python
- Установка флага
- Ограничивается символами ASCII:
re.ASCII
- Не чувствителен к регистру:
re.IGNORECASE
- Сопоставьте начало и конец каждой строки:
re.MULTILINE
- Укажите несколько флагов
- Ограничивается символами ASCII:
- Жадные и нежадные матчи
- Составьте шаблон регулярного выражения: compile()
- объект соответствия
- Проверьте, совпадает ли начало строки, извлеките: match()
- Проверка совпадений, не ограниченных началом, извлечение: search()
- Проверьте, совпадает ли вся строка: fullmatch()
- Получение списка всех совпадающих частей: findall()
- Получение всех совпадающих частей в виде итератора: finditer()
- Замените подходящие части: sub(), subn()
- Разделение строк с помощью шаблонов регулярных выражений: split()
- Метасимволы регулярных выражений, специальные последовательности и предостережения в Python
- Установка флага
- Жадные и нежадные матчи
Составьте шаблон регулярного выражения: 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
Обратите внимание, что жадное соответствие по умолчанию может совпасть с неожиданными строками.