Введение
Регулярное выражение, или regex, — это мощный инструмент для обработки текста. Это последовательность символов, которая определяет шаблон поиска, позволяя находить, сопоставлять и управлять текстом с невероятной гибкостью. В Python функциональность регулярных выражений обрабатывается модулем re.
В этой лабораторной работе вы изучите основы использования регулярных выражений в Python. Мы начнем с основ сопоставления шаблонов и постепенно перейдем к более продвинутым темам, таким как группировка, использование квантификаторов для повторений, определение классов символов и использование мощных методов и флагов для обработки сложных сценариев сопоставления. К концу вы будете уверенно использовать regex для выполнения сложных строковых операций.
Введение в Сопоставление Регулярных Выражений
На этом шаге вы изучите базовый рабочий процесс использования регулярных выражений в Python. Этот процесс обычно включает четыре основных этапа:
- Импортировать модуль
re. - Создать объект Regex с помощью
re.compile(). Рекомендуется использовать "сырую" строку (r"..."), чтобы предотвратить интерпретацию обратных слешей как управляющих символов. - Использовать метод
search()объекта Regex для поиска шаблона в строке. Если шаблон найден, метод возвращает объектMatch. - Использовать метод
group()объектаMatchдля получения фактически сопоставленного текста.
Сначала рассмотрим некоторые распространенные символы регулярных выражений, которые вы будете использовать.
| Символ | Соответствует |
|---|---|
? |
Ноль или одно вхождение предыдущей группы. |
* |
Ноль или более вхождений предыдущей группы. |
+ |
Одно или более вхождений предыдущей группы. |
{n} |
Ровно $n$ вхождений предыдущей группы. |
^spam |
Строка должна начинаться с spam. |
spam$ |
Строка должна заканчиваться на spam. |
. |
Любой символ, кроме символа новой строки. |
\d, \w, и \s |
Соответственно, цифра, слово (буквенно-цифровой символ) или пробельный символ. |
\D, \W, и \S |
Соответственно, любой символ, кроме цифры, слова или пробела. |
[abc] |
Любой символ, находящийся между квадратными скобками (например, a, b, или c). |
[^abc] |
Любой символ, который не находится между квадратными скобками. |
Теперь применим это на практике. Мы напишем скрипт для поиска номера телефона в строке. Шаблон для номера телефона США — XXX-XXX-XXXX, где $X$ — это цифра. В регулярных выражениях \d обозначает цифру.
Примечание: Если вы не знакомы с использованием WebIDE (похожего на VS Code), пожалуйста, изучите его сначала в этой лаборатории.
Откройте файл main.py в проводнике файлов слева и добавьте следующий код.
## Import re module for regular expression operations
import re
## re.compile(): create regex pattern object (use raw string r'' to avoid escaping)
## The pattern \d\d\d-\d\d\d-\d\d\d\d matches three digits, a hyphen, three digits, a hyphen, and four digits.
phone_num_regex = re.compile(r'\d\d\d-\d\d\d-\d\d\d\d')
## The search() method scans the string, looking for the first location where this regex produces a match.
mo = phone_num_regex.search('My number is 415-555-4242.')
## The group() method returns the string of the matched text.
print(f'Phone number found: {mo.group()}')
Чтобы запустить скрипт, откройте терминал и выполните следующую команду:
python3 main.py
Вы должны увидеть найденный номер телефона, напечатанный в консоли.
Phone number found: 415-555-4242
Этот базовый рабочий процесс является основой для всех операций с регулярными выражениями в Python.
Группировка и Альтернация
На этом шаге вы узнаете, как выделять определенные части сопоставленного текста с помощью групп и как сопоставлять один из нескольких возможных шаблонов с помощью альтернации.
Группировка с помощью круглых скобок ()
Круглые скобки () используются для создания групп внутри регулярного выражения. Это полезно, когда вы хотите извлечь части совпадения, например, код города из номера телефона. Вы можете получить доступ к отдельным группам с помощью group(n), где group(1) — первая группа, group(2) — вторая, и так далее. group(0) или group() возвращает всю сопоставленную строку.
Давайте модифицируем main.py, чтобы отделить код города от остальной части номера телефона.
import re
## Parentheses create groups: group(1) for the first group, group(2) for the second.
## The first group (\d\d\d) captures the area code.
## The second group (\d\d\d-\d\d\d\d) captures the main number.
phone_num_regex = re.compile(r'(\d\d\d)-(\d\d\d-\d\d\d\d)')
mo = phone_num_regex.search('My number is 415-555-4242.')
## To retrieve all groups at once, use the groups() method, which returns a tuple.
area_code, main_number = mo.groups()
print(f"Area Code: {area_code}")
print(f"Main Number: {main_number}")
print(f"Full Match: {mo.group(0)}")
Запустите скрипт:
python3 main.py
Вывод покажет разделенные части номера:
Area Code: 415
Main Number: 555-4242
Full Match: 415-555-4242
Альтернация с помощью вертикальной черты |
Символ вертикальной черты | действует как оператор "ИЛИ". Он позволяет сопоставить одно из нескольких выражений. Например, Batman|Tina Fey сопоставит либо "Batman", либо "Tina Fey".
Вы также можете использовать вертикальную черту внутри группы для сопоставления одного из нескольких возможных шаблонов как части вашего regex. Давайте попробуем сопоставить различные термины, связанные с "Bat-". Обновите ваш main.py следующим кодом:
import re
## The pipe | matches one of several patterns within a group.
## Here, it matches 'man', 'mobile', 'copter', or 'bat'.
bat_regex = re.compile(r'Bat(man|mobile|copter|bat)')
mo = bat_regex.search('Batmobile lost a wheel')
print(f"Full match: {mo.group()}")
print(f"Group 1: {mo.group(1)}")
Запустите скрипт снова:
python3 main.py
Вы увидите, что regex сопоставил "Batmobile", а первая группа захватила "mobile".
Full match: Batmobile
Group 1: mobile
Повторение с Квантификаторами
На этом шаге мы рассмотрим квантификаторы — специальные символы, которые позволяют указать, сколько раз должна повторяться предшествующая группа.
Необязательное сопоставление с помощью ?
Вопросительный знак ? делает предшествующую группу необязательной, что означает, что она будет соответствовать ноль или один раз. Это идеально подходит для шаблонов, которые могут присутствовать или отсутствовать. Например, мы можем сопоставить как "Batman", так и "Batwoman".
Обновите main.py следующим кодом:
import re
## The (wo)? group is optional. It will match 'wo' if it exists, or match nothing if it doesn't.
bat_regex = re.compile(r'Bat(wo)?man')
mo1 = bat_regex.search('The Adventures of Batman')
print(mo1.group())
mo2 = bat_regex.search('The Adventures of Batwoman')
print(mo2.group())
Запустите скрипт:
python3 main.py
Вывод показывает, что обе строки были успешно сопоставлены:
Batman
Batwoman
Ноль или более с помощью *
Звездочка * (или "звезда") соответствует предшествующей группе ноль или более раз. Это полезно, когда шаблон может повторяться любое количество раз, включая ноль раз.
import re
## The (wo)* group can appear zero or more times.
bat_regex = re.compile(r'Bat(wo)*man')
mo1 = bat_regex.search('The Adventures of Batman') ## Zero 'wo'
print(mo1.group())
mo2 = bat_regex.search('The Adventures of Batwowowowoman') ## Four 'wo's
print(mo2.group())
Один или более с помощью +
Знак плюс + соответствует предшествующей группе один или более раз. В отличие от звезды, группа должна появиться как минимум один раз.
import re
## The (wo)+ group must appear at least once.
bat_regex = re.compile(r'Bat(wo)+man')
mo1 = bat_regex.search('The Adventures of Batwoman') ## One 'wo'
print(mo1.group())
mo2 = bat_regex.search('The Adventures of Batman') ## Zero 'wo's
print(mo2 is None) ## This will print True because no match is found.
Конкретные повторения с помощью {}
Фигурные скобки {} позволяют указать точное количество повторений или диапазон.
{3}соответствует ровно три раза.{3,5}соответствует от трех до пяти раз.{3,}соответствует три или более раз.{,5}соответствует до пяти раз.
import re
## (Ha){3} matches 'Ha' repeated exactly 3 times.
ha_regex = re.compile(r'(Ha){3}')
mo1 = ha_regex.search('HaHaHa')
print(mo1.group())
Жадное и Нежадное сопоставление (Greedy and Non-Greedy Matching)
По умолчанию regex в Python является "жадным" (greedy), что означает, что он сопоставляет максимально длинную возможную строку. Чтобы сделать его "нежадным" (non-greedy, сопоставляющим кратчайшую возможную строку), добавьте вопросительный знак ? после квантификатора.
Посмотрим на разницу. Очистите main.py и добавьте следующее:
import re
## Greedy match: {3,5} matches the longest possible string (5 'Ha's).
greedy_ha_regex = re.compile(r'(Ha){3,5}')
mo1 = greedy_ha_regex.search('HaHaHaHaHa')
print(f"Greedy match: {mo1.group()}")
## Non-greedy match: {3,5}? matches the shortest possible string (3 'Ha's).
non_greedy_ha_regex = re.compile(r'(Ha){3,5}?')
mo2 = non_greedy_ha_regex.search('HaHaHaHaHa')
print(f"Non-greedy match: {mo2.group()}")
Запустите скрипт:
python3 main.py
Вывод наглядно демонстрирует разницу:
Greedy match: HaHaHaHaHa
Non-greedy match: HaHaHa
Классы Символов, Якоря и Шаблоны (Wildcards)
На этом шаге вы изучите более продвинутые методы, включая поиск всех совпадений, создание пользовательских наборов символов, привязку совпадений к началу или концу строки и использование шаблонов (wildcards).
Метод findall()
В то время как search() возвращает только первое совпадение, метод findall() возвращает список всех непересекающихся совпадений в строке. Если в регулярном выражении есть группы, findall() возвращает список кортежей (tuples), где каждый кортеж содержит строки для каждой группы.
Обновите main.py этим примером:
import re
## If the regex has no groups, findall() returns a list of strings.
phone_num_regex = re.compile(r'\d\d\d-\d\d\d-\d\d\d\d')
matches = phone_num_regex.findall('Cell: 415-555-9999 Work: 212-555-0000')
print(matches)
Запустите скрипт, чтобы увидеть вывод:
python3 main.py
['415-555-9999', '212-555-0000']
Создание Собственных Классов Символов []
Квадратные скобки [] позволяют определить пользовательский класс символов, который будет соответствовать любому одиночному символу внутри него. Например, [aeiou] соответствует любой строчной гласной. Вы также можете указать диапазон, например, [a-z] для любой строчной буквы или [0-9] для любой цифры.
Карет ^ в качестве первого символа внутри скобок создает отрицающий класс символов, который соответствует любому символу, не входящему в класс.
Давайте найдем все гласные в строке. Обновите main.py:
import re
## [aeiouAEIOU] matches any vowel, lowercase or uppercase.
vowel_regex = re.compile(r'[aeiouAEIOU]')
vowels = vowel_regex.findall('Robocop eats baby food. BABY FOOD.')
print(vowels)
Якоря: Карет ^ и Знак Доллара $
Якоря используются для утверждения позиции в строке.
- Карет
^соответствует началу строки. - Знак доллара
$соответствует концу строки.
При использовании вместе (^...$) они гарантируют, что вся строка должна соответствовать шаблону.
import re
## ^Hello matches strings that start with 'Hello'.
begins_with_hello = re.compile(r'^Hello')
match1 = begins_with_hello.search('Hello world!')
print(match1 is not None) ## True
## \d+$ matches strings that end with one or more digits.
ends_with_number = re.compile(r'\d+$')
match2 = ends_with_number.search('Your number is 42')
print(match2.group()) ## '42'
## ^\d+$ matches strings that consist only of digits.
whole_string_is_num = re.compile(r'^\d+$')
match3 = whole_string_is_num.search('1234567890')
print(match3 is not None) ## True
Символ Шаблона (Wildcard) .
Точка . (или шаблонный символ) соответствует любому одиночному символу, кроме символа новой строки. Это полезное сокращение для "любого символа".
Давайте найдем все трехбуквенные слова, оканчивающиеся на "at". Обновите main.py:
import re
## The . character matches any character (except newline).
## So, .at matches 'cat', 'hat', 'sat', etc.
at_regex = re.compile(r'.at')
matches = at_regex.findall('The cat in the hat sat on the flat mat.')
print(matches)
Запустите скрипт:
python3 main.py
Вывод будет списком всех сопоставленных слов:
['cat', 'hat', 'sat', 'lat', 'mat']
Расширенные Методы и Флаги
На этом заключительном шаге мы рассмотрим некоторые мощные методы и флаги, которые изменяют поведение ваших регулярных выражений, делая их еще более универсальными.
Сопоставление Всего с помощью Точка-Звездочка .*
Комбинация .* — это распространенный шаблон, который соответствует любому символу (.), повторяющемуся ноль или более раз (*). Он используется для сопоставления любой последовательности символов. По умолчанию он является жадным (greedy) и сопоставит максимально возможный текст.
import re
## The pattern (.*) captures everything between 'First Name: ' and ' Last Name: '.
name_regex = re.compile(r'First Name: (.*) Last Name: (.*)')
mo = name_regex.search('First Name: Al Last Name: Sweigart')
print(f"First Name: {mo.group(1)}")
print(f"Last Name: {mo.group(2)}")
Чтобы сделать его нежадным (non-greedy), используйте .*?. Это сопоставит кратчайшую возможную строку.
Сопоставление Новых Строк с помощью re.DOTALL
По умолчанию символ точки . не соответствует символам новой строки (\n). Чтобы это изменить, вы можете передать re.DOTALL в качестве второго аргумента в re.compile(). Этот флаг позволяет точке соответствовать всем символам, включая символы новой строки.
import re
## With re.DOTALL, '.*' will match across multiple lines.
newline_regex = re.compile('.*', re.DOTALL)
mo = newline_regex.search('Serve the public trust.\nProtect the innocent.\nUphold the law.')
print(mo.group())
Сопоставление Без Учета Регистра с помощью re.IGNORECASE
Чтобы ваше регулярное выражение игнорировало регистр (например, сопоставляло "cat", "Cat" и "CAT"), передайте re.IGNORECASE или его сокращение re.I в качестве второго аргумента в re.compile().
import re
## re.I makes the pattern 'robocop' match 'Robocop', 'ROBOCOP', etc.
robocop = re.compile(r'robocop', re.I)
match = robocop.search('Robocop is part man, part machine, all cop.')
print(match.group())
Замена Строк с помощью Метода sub()
Метод sub() находит все вхождения шаблона и заменяет их указанной строкой. Он принимает два аргумента: строку для замены и строку для поиска.
Давайте заменим имена агентов на "CENSORED". Обновите main.py:
import re
## The pattern 'Agent \w+' matches 'Agent ' followed by one or more word characters.
names_regex = re.compile(r'Agent \w+')
result = names_regex.sub('CENSORED', 'Agent Alice gave the secret documents to Agent Bob.')
print(result)
Запустите скрипт:
python3 main.py
Вывод покажет замененную строку:
CENSORED gave the secret documents to CENSORED.
Управление Сложными Регулярными Выражениями с помощью re.VERBOSE
Когда шаблоны регулярных выражений становятся сложными, их трудно читать. Передавая 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})?)')
Превращается в эту читаемую версию с re.VERBOSE:
import re
phone_regex = re.compile(r'''(
(\d{3} | \(\d{3}\))? ## area code (optional)
(\s | - | \.)? ## separator (optional)
\d{3} ## first 3 digits
(\s | - | \.) ## separator
\d{4} ## last 4 digits
(\s*(ext|x|ext.)\s*\d{2,5})? ## extension (optional)
)''', re.VERBOSE)
mo = phone_regex.search("My phone number is 415-555-4242 ext. 55")
if mo:
print(f"Found: {mo.group()}")
Это делает ваш код намного более поддерживаемым.
Резюме
В этой лабораторной работе вы освоили основы регулярных выражений в Python с использованием модуля re. Вы изучили полный рабочий процесс: от компиляции шаблона с помощью re.compile() до поиска совпадений с помощью search() и извлечения текста с помощью group().
Вы изучили ключевые концепции, включая:
- Группировку с помощью
()для захвата определенных частей совпадения. - Чередование (Альтернативу) с помощью
|для сопоставления одного из нескольких шаблонов. - Квантификаторы, такие как
?,*,+и{}, для управления повторениями. - Жадное (Greedy) против Нежадного (Non-greedy) сопоставление для контроля длины совпадений.
- Расширенные инструменты, такие как
findall()для получения всех совпадений, классы символов[]для пользовательских наборов и якоря^и$для привязки совпадений к границам строки. - Мощные флаги, такие как
re.DOTALLдля многострочного сопоставления,re.IGNORECASEдля игнорирования регистра иre.VERBOSEдля написания чистых регулярных выражений с комментариями. - Замену строк с помощью метода
sub().
С этими навыками вы теперь готовы решать широкий спектр задач по обработке текста, от простой проверки данных до сложного извлечения и манипулирования данными.



