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

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

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

  1. Импортируйте модуль regex с помощью import re.
  2. Создайте объект Regex с помощью функции re.compile(). (Не забудьте использовать необработанную строку (raw string).)
  3. Передайте строку, которую вы хотите искать, в метод search() объекта Regex. Это возвращает объект Match.
  4. Вызовите метод group() объекта Match, чтобы вернуть строку фактически сопоставленного текста.

Все функции regex в Python находятся в модуле re:

# Import re module for regular expression operations
import re

Символы Regex

СимволСоответствует
?ноль или одно из предыдущей группы.
*ноль или более из предыдущей группы.
+один или более из предыдущей группы.
{n}ровно n из предыдущей группы.
{n,}n или более из предыдущей группы.
{,m}от 0 до m из предыдущей группы.
{n,m}не менее n и не более m предыдущей группы.
{n,m}? или *? или +?выполняет нежадный (non-greedy) поиск предыдущей группы.
^spamозначает, что строка должна начинаться с spam.
spam$означает, что строка должна заканчиваться на spam.
.любой символ, кроме символов новой строки.
\d, \w, и \sцифра, слово или пробельный символ соответственно.
\D, \W, и \Sчто угодно, кроме цифры, слова или пробела соответственно.
[abc]любой символ между скобками (например, a, b, ).
[^abc]любой символ, которого нет между скобками.

Объекты сопоставления regex

# re.compile(): create regex pattern object (use raw string r'' to avoid escaping)
phone_num_regex = re.compile(r'\d\d\d-\d\d\d-\d\d\d\d')  # Pattern: 3 digits-3 digits-4 digits

mo = phone_num_regex.search('My number is 415-555-4242.')  # Search for pattern

print(f'Phone number found: {mo.group()}')  # group() returns matched text
Phone number found: 415-555-4242

Группировка с помощью скобок

# Parentheses create groups: group(1) returns first group, group(2) returns second
phone_num_regex = re.compile(r'(\d\d\d)-(\d\d\d-\d\d\d\d)')  # Two groups in parentheses
mo = phone_num_regex.search('My number is 415-555-4242.')

mo.group(1)  # Returns first group: '415'
'415'
mo.group(2)
'555-4242'
mo.group(0)
'415-555-4242'
mo.group()
'415-555-4242'
Викторина

Войдите в систему, чтобы ответить на эту викторину и отслеживать свой прогресс обучения

What does group() return when called on a match object?
A. The entire matched text
B. The first group only
C. All groups as a list
D. The index of the match

Чтобы получить все группы сразу, используйте метод groups():

# groups(): returns tuple of all groups
mo.groups()  # Returns ('415', '555-4242')
('415', '555-4242')
area_code, main_number = mo.groups()

print(area_code)
415
print(main_number)
555-4242

Несколько групп с оператором “ИЛИ” (Pipe)

Вы можете использовать символ | везде, где хотите сопоставить одно из нескольких выражений.

hero_regex = re.compile (r'Batman|Tina Fey')

mo1 = hero_regex.search('Batman and Tina Fey.')
mo1.group()
'Batman'
mo2 = hero_regex.search('Tina Fey and Batman.')
mo2.group()
'Tina Fey'

Вы также можете использовать оператор “ИЛИ” для сопоставления одного из нескольких шаблонов как части вашего regex:

bat_regex = re.compile(r'Bat(man|mobile|copter|bat)')
mo = bat_regex.search('Batmobile lost a wheel')

mo.group()
'Batmobile'
mo.group(1)
'mobile'

Необязательное сопоставление с помощью знака вопроса

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

bat_regex = re.compile(r'Bat(wo)?man')

mo1 = bat_regex.search('The Adventures of Batman')
mo1.group()
'Batman'
mo2 = bat_regex.search('The Adventures of Batwoman')
mo2.group()
'Batwoman'

Сопоставление нуля или более с помощью Звездочки

* (звездочка или астериск) означает «сопоставить ноль или более». Группа, предшествующая звездочке, может встречаться в тексте любое количество раз.

bat_regex = re.compile(r'Bat(wo)*man')
mo1 = bat_regex.search('The Adventures of Batman')
mo1.group()
'Batman'
mo2 = bat_regex.search('The Adventures of Batwoman')
mo2.group()
'Batwoman'
mo3 = bat_regex.search('The Adventures of Batwowowowoman')
mo3.group()
'Batwowowowoman'

Сопоставление одного или более с помощью Плюса

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

bat_regex = re.compile(r'Bat(wo)+man')

mo1 = bat_regex.search('The Adventures of Batwoman')
mo1.group()
'Batwoman'
mo2 = bat_regex.search('The Adventures of Batwowowowoman')
mo2.group()
'Batwowowowoman'
mo3 = bat_regex.search('The Adventures of Batman')
mo3 is None
True

Сопоставление конкретных повторений с помощью фигурных скобок

Если у вас есть группа, которую вы хотите повторить определенное количество раз, следуйте за группой в вашем regex числом в фигурных скобках:

ha_regex = re.compile(r'(Ha){3}')

mo1 = ha_regex.search('HaHaHa')
mo1.group()
'HaHaHa'
mo2 = ha_regex.search('Ha')
mo2 is None
True

Вместо одного числа вы можете указать диапазон с минимумом и максимумом между фигурными скобками. Например, regex (Ha){3,5} сопоставит ‘HaHaHa’, ‘HaHaHaHa’ и ‘HaHaHaHaHa’.

ha_regex = re.compile(r'(Ha){2,3}')
mo1 = ha_regex.search('HaHaHaHa')
mo1.group()
'HaHaHa'

Жадное и нежадное сопоставление

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

greedy_ha_regex = re.compile(r'(Ha){3,5}')

mo1 = greedy_ha_regex.search('HaHaHaHaHa')
mo1.group()
'HaHaHaHaHa'
non_greedy_ha_regex = re.compile(r'(Ha){3,5}?')
mo2 = non_greedy_ha_regex.search('HaHaHaHaHa')
mo2.group()
'HaHaHa'
Викторина

Войдите в систему, чтобы ответить на эту викторину и отслеживать свой прогресс обучения

What makes a regex pattern non-greedy?
A. Using _ instead of +
B. Adding a ? after the quantifier (e.g., _?, +?, {3,5}?)
C. Using parentheses
D. Using square brackets

Метод findall()

Метод findall() вернет строки каждого совпадения в искомой строке.

phone_num_regex = re.compile(r'\d\d\d-\d\d\d-\d\d\d\d') # has no groups

phone_num_regex.findall('Cell: 415-555-9999 Work: 212-555-0000')
['415-555-9999', '212-555-0000']

Создание собственных классов символов

Вы можете определить свой собственный класс символов, используя квадратные скобки. Например, класс символов [aeiouAEIOU] будет соответствовать любой гласной, как строчной, так и заглавной.

vowel_regex = re.compile(r'[aeiouAEIOU]')
vowel_regex.findall('Robocop eats baby food. BABY FOOD.')
['o', 'o', 'o', 'e', 'a', 'a', 'o', 'o', 'A', 'O', 'O']

Вы также можете включать диапазоны букв или цифр, используя дефис. Например, класс символов [a-zA-Z0-9] будет соответствовать всем строчным буквам, заглавным буквам и цифрам.

Поместив символ каретки (^) сразу после открывающей скобки класса символов, вы можете создать отрицательный класс символов, который будет соответствовать всем символам, которых нет в классе символов:

consonant_regex = re.compile(r'[^aeiouAEIOU]')
consonant_regex.findall('Robocop eats baby food. BABY FOOD.')
['R', 'b', 'c', 'p', ' ', 't', 's', ' ', 'b', 'b', 'y', ' ', 'f', 'd', '.', ' ', 'B', 'B', 'Y', ' ', 'F', 'D', '.']

Символы Каретки и Знака доллара

  • Вы также можете использовать символ каретки ^ в начале regex, чтобы указать, что совпадение должно произойти в начале искомого текста.

  • Аналогично, вы можете поместить знак доллара $ в конец regex, чтобы указать, что строка должна заканчиваться этим шаблоном regex.

  • И вы можете использовать ^ и $ вместе, чтобы указать, что вся строка должна соответствовать regex.

Строка регулярного выражения r'^Hello' соответствует строкам, начинающимся с ‘Hello’:

begins_with_hello = re.compile(r'^Hello')
begins_with_hello.search('Hello world!')
<_sre.SRE_Match object; span=(0, 5), match='Hello'>
begins_with_hello.search('He said hello.') is None
True

Строка регулярного выражения r'\d\$' соответствует строкам, которые заканчиваются числовым символом от 0 до 9:

whole_string_is_num = re.compile(r'^\d+$')

whole_string_is_num.search('1234567890')
<_sre.SRE_Match object; span=(0, 10), match='1234567890'>
whole_string_is_num.search('12345xyz67890') is None
True
whole_string_is_num.search('12 34567890') is None
True

Символ подстановки (Wildcard)

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

at_regex = re.compile(r'.at')

at_regex.findall('The cat in the hat sat on the flat mat.')
['cat', 'hat', 'sat', 'lat', 'mat']

Сопоставление всего с помощью Dot-Star

name_regex = re.compile(r'First Name: (.*) Last Name: (.*)')

mo = name_regex.search('First Name: Al Last Name: Sweigart')
mo.group(1)
'Al'
mo.group(2)
'Sweigart'

.* использует жадный режим: он всегда будет пытаться сопоставить как можно больше текста. Чтобы сопоставить любой текст в нежадном режиме, используйте точку, звездочку и вопросительный знак (.*?). Вопросительный знак указывает Python сопоставлять нежадным образом:

non_greedy_regex = re.compile(r'<.*?>')
mo = non_greedy_regex.search('<To serve man> for dinner.>')
mo.group()
'<To serve man>'
greedy_regex = re.compile(r'<.*>')
mo = greedy_regex.search('<To serve man> for dinner.>')
mo.group()
'<To serve man> for dinner.>'

Сопоставление новых строк с помощью символа Dot

Dot-star сопоставляет все, кроме новой строки. Передавая re.DOTALL в качестве второго аргумента в re.compile(), вы можете заставить символ точки сопоставлять все символы, включая символ новой строки:

no_newline_regex = re.compile('.*')
no_newline_regex.search('Serve the public trust.\nProtect the innocent.\nUphold the law.').group()
'Serve the public trust.'
newline_regex = re.compile('.*', re.DOTALL)
newline_regex.search('Serve the public trust.\nProtect the innocent.\nUphold the law.').group()
'Serve the public trust.\nProtect the innocent.\nUphold the law.'

Регистронезависимое сопоставление

Чтобы сделать ваше regex регистронезависимым, вы можете передать re.IGNORECASE или re.I в качестве второго аргумента в re.compile():

robocop = re.compile(r'robocop', re.I)

robocop.search('Robocop is part man, part machine, all cop.').group()
'Robocop'
robocop.search('ROBOCOP protects the innocent.').group()
'ROBOCOP'
robocop.search('Al, why does your programming book talk about robocop so much?').group()
'robocop'

Замена строк с помощью метода sub()

Метод sub() для объектов Regex принимает два аргумента:

  1. Первый аргумент — это строка для замены любых совпадений.
  2. Второй — это строка для регулярного выражения.

Метод sub() возвращает строку с примененными заменами:

names_regex = re.compile(r'Agent \w+')

names_regex.sub('CENSORED', 'Agent Alice gave the secret documents to Agent Bob.')
'CENSORED gave the secret documents to CENSORED.'
Викторина

Войдите в систему, чтобы ответить на эту викторину и отслеживать свой прогресс обучения

What does the sub() method do?
A. Finds all matches in a string
B. Replaces all matches with a replacement string
C. Splits a string at matches
D. Validates a string format

Управление сложными Regex

Чтобы указать функции re.compile() игнорировать пробелы и комментарии внутри строки регулярного выражения, можно включить «подробный режим» (verbose mode), передав переменную re.VERBOSE в качестве второго аргумента в re.compile().

Теперь вместо трудночитаемого регулярного выражения, такого как это:

phone_regex = re.compile(r'((\d{3}|\(\d{3}\))?(\s|-|\.)?\d{3}(\s|-|\.)\d{4}(\s*(ext|x|ext.)\s*\d{2,5})?)')

вы можете разбить регулярное выражение на несколько строк с комментариями, как показано ниже:

phone_regex = re.compile(r'''(
    (\d{3}|\(\d{3}\))?            # area code
    (\s|-|\.)?                    # separator
    \d{3}                         # first 3 digits
    (\s|-|\.)                     # separator
    \d{4}                         # last 4 digits
    (\s*(ext|x|ext.)\s*\d{2,5})?  # extension
    )''', re.VERBOSE)
Викторина

Войдите в систему, чтобы ответить на эту викторину и отслеживать свой прогресс обучения

What does re.VERBOSE do when passed to re.compile()?
A. Allows whitespace and comments in the regex pattern for better readability
B. Makes the regex case-insensitive
C. Makes the dot match newline characters
D. Speeds up regex matching

Соответствующие ссылки