С помощью регулярных выражений в Python можно быстро анализировать строки, обрабатывать текст и проверять корректность пользовательского ввода. В этой статье разберемся с базовыми принципами регулярных выражений и решим практические задачи.
Что такое регулярные выражения?
Регулярные выражения представляют собой мощные шаблоны для поиска фрагментов в тексте. К примеру, в коде Python-программы есть форма регистрации пользователей. Для регистрации надо обязательно ввести адрес электронной почты. При этом в поле можно ввести совершенно любую строку и нарушить работу программы. Поэтому каким-то образом надо проверить валидность данных.
Первая идея — проверять строку на наличие символа at (@), ведь он есть в каждом адресе электронной почты, но пользователь может ввести email@. Символ есть, но это все еще не адрес электронной почты.
Регулярные выражения в Python — это шаблоны, по которым можно проверять валидность данных, искать совпадения в тексте или заменять определенные фрагменты. Плюс таких выражений в том, что они поддерживаются практически во всех языках программирования. Изучив основной принцип работы, можно будет писать «регулярки» в коде любого другого проекта. Регулярное выражение для проверки валидности адреса электронной почты в Python будет выглядеть так:
r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$'
Напишем код небольшой функции для проверки валидности адресов электронной почты. Наше регулярное выражение поместим в переменную email_pattern, с помощью функции match() будем искать совпадения и возвращать True или False, в зависимости от того, является ли строка адресом электронной почты.
import re email_pattern = r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$' def is_valid_email(email): return re.match(email_pattern, email) is not None
Теперь создадим несколько переменных с адресами и проверим каждый нашей функцией:
email1 = "user@example.com" email2 = "invalid_email@" email3 = "another.user123@subdomain.domain" print(is_valid_email(email1)) # True print(is_valid_email(email2)) # False print(is_valid_email(email3)) # True
Наше регулярное выражение может проверять не только простые адреса типа user@example.com, но и более сложные с поддоменами. Важно отметить, что сам процесс проверки валидности адресов электронной почты не самый простой. Все из-за того, что существует множество доменных зон и вариантов записи имени пользователя.
Регулярные выражения в Python
Регулярные выражения или RegEx — строки, задающие шаблон для поиска фрагментов в тексте. По сути, RegEx можно назвать узкоспециализированным языком программирования, встроенным в Python. У регулярных выражений есть свой строгий синтаксис, набор функций для проверки совпадений и изменения исходной строки.
В Python регулярные выражения доступны в модуле re, который необходимо импортировать в начале файла с помощью import re. Для экранирования обычно используют символ обратного слэша (\) или raw-строки (r»). Рекомендуется использовать второй способ, так как в больших регулярных выражениях и так будет довольно много слэшей.
Функции регулярных выражений в Python
re.match() — поиск вхождения шаблона в начало строки. В качестве аргументов требуется передать сначала шаблон, а потом строку для проверки:
import re str = "Банкиров ребрендили-ребрендили-ребрендили, да не выребрендировали" print(re.match(r'ре', str))
Если запустить этот код, то Python выведет None, несмотря на то что «ре» в строке встречается восемь раз. Все из-за того, что re.match() ищет вхождение шаблона именно в начале строки, а не в любом месте. Можно исправить r’ре’ на r’Ба’, тогда получим нужный результат. Важно отметить, что регулярные выражения чувствительны к регистру, это значит, что r’Ба’ и r’ба’ — разные строки. Эту особенность надо учитывать при написании выражений. Для игнорирования регистра символов можно передать в функцию флаг re.IGNORECASE.
re.search() — ищет первое вхождение шаблона в любом месте строки, возвращая объект match, если в строке есть другие подходящие фрагменты, то они будут проигнорированы. У функции re.search() есть дополнительные функции, упрощающие поиск:
- .group() — возвращает тот фрагмент строки, в котором нашлось совпадение;
- .span() — возвращает кортеж с начальной и конечной позицией искомого шаблона;
- .string() — возвращает строку, которую передали в re.search().
re.findall() — поиск всех вхождений шаблона в любом месте строки:
import re str = "Банкиров ребрендили-ребрендили-ребрендили, да не выребрендировали" print(re.findall(r'ре', str, re.IGNORECASE)) >>> ['ре', 'ре', 'ре', 'ре', 'ре', 'ре', 'ре', 'ре']
re.sub() — заменяет фрагменты в соответствии с шаблоном:
import re string = "шел дождь и два студента" result = re.sub(r'два', 'три', string) print(result) >>> шел дождь и три студента
re.split() — разделяет строку по шаблону, количество разделений задается числом:
import re string = "шел дождь и два студента" result = re.split(r' ', string, 4) print(result) >>> ['шел', 'дождь', 'и', 'два', 'студента']
import re string = "шел дождь и два студента" result = re.split(r' ', string, 1) print(result) >>> ['шел', 'дождь и два студента']
Эти функции реализуют базовые возможности регулярных выражений в Python.
Метасимволы регулярных выражений в Python
В примерах выше мы использовали регулярные выражения для поиска конкретных слов и слогов, но возможности технологии предоставляют работу с более продвинутыми шаблонами. К примеру, можно искать последовательности символов или любые символы из заданного диапазона. Сделать это можно с помощью специальных метасимволов.
Метасимвол | Зачем нужен | Как использовать |
. | Задает один произвольный символ, кроме новой строки | E.am.le |
[…] | Любой символ в скобках, сами символы можно задавать с помощью перечислений или диапазонов | [123abc] — только указанные символы[A-Za-z0-9] — весь английский алфавит и цифры[А-ЯЁа-яё0-9] — весь русский алфавит и цифры |
[^…] | Любой символ, исключая указанные в скобках, сами символы можно задавать с помощью перечислений или диапазонов | [^A-Za-z] |
^ | Начало строки | ^Hello |
$ | Конец строки | Hello$ |
| | Логический оператор ИЛИ. Поиск одного из нескольких указанных вариантов | [0-9]|[IVXLCDM] — арабские или римские цифры |
\ | Экранирование, с помощью которого Python понимает, является ли следующий символ обычным или специальным. Можно обычные символы превращать в метасимволы и обратно | \[\.\] — метасимволы становятся обычными\d\W\A — обычные символы становятся метасимволами |
* | Произвольное число повторений одного символа | Hello* |
? | Строго одно повторение символа | Hello? |
(…) | Группировка символов в скобках | ([A-Z][A-Za-z]+) |
{…} | Число повторений предыдущего символа | Hello{4} — строка четыре раза подрядHello{1,4} — строка от одного до четырех раз подрядHello{4,} — строка от четырех раз подрядHello{,4} — строка от нуля до четырех раз подряд |
Важно отметить, что буквы «Ё» и «ё» не входят в диапазоны [А-Я] и [а-я]. Поэтому их надо отдельно включать, иначе регулярное выражение будет работать с ошибками.
Выше мы говорили про то, что обратный слэш (\) может превращать метасимволы в обычные и наоборот. К примеру, обычная точка (.) будет обозначать произвольный символ, а точка с обратным слэшем (\.) — просто точку. Такой принцип работает и с буквами латинского алфавита, которые становятся метасимволами.
Символ | Зачем нужен |
\d | Любая цифра, заменяет собой запись [0-9] |
\D | Исключает все цифры, заменяет собой запись [^0-9] |
\s | Любой пробельный символ, включая пробел, табуляцию, новую строку и возврат каретки |
\S | Любой символ, исключая пробельный |
\w | Любая буква, цифра и знак нижнего подчеркивания (_) |
\W | Любой символ, кроме буквы, цифры и нижнего подчеркивания |
\A | Начало строки, заменяет собой запись ^ |
\Z | Конец строки, заменяет собой запись $ |
\b | Начало или конец слова |
\B | Середина слова |
\n | Новая строка |
\t | Табуляция |
\r | Возврат каретки |
Флаги регулярных выражений в Python
Расширить возможность регулярных выражений в Python можно с помощью специальных флагов, которые передаются в функцию. С флагом re.IGNORECASE, игнорирующим регистр символов, мы уже познакомились выше. Но есть и другие флаги, ускоряющие работу с «регулярками».
Краткая запись флага | Полная запись флага | Зачем нужен |
re.I | re.IGNORECASE | Игнорирует регистр символов |
re.A | re.ASCII | Возвращает совпадения только по таблице ASCII-символов |
re.X | re.VERBOSE | Позволяет использовать комментарии в регулярных выражениях |
re.S | re.DOTALL | Метасимвол точка (.) возвращает совпадения по всем символам, включая новую строку. |
re.L | re.LOCALE | Добавляет к \w, \W, \b, \B, \s и \S региональные настройки, но работает только с байтовыми строками |
re.M | re.MULTILINE | Возвращает совпадения в начале каждой новой строки, если используется с ^, и в конце каждой новой строки — если с $ |
Практические задачи
В регулярных выражениях мало теории, и освоить ее может даже новичок, который уже уверенно пишет простой код на Python. Больше времени уйдет, чтобы довести до автоматизма навык составлять сложные регулярные выражения. Еще больше усилий надо будет приложить, чтобы научиться читать и понимать чужие «регулярки». Добиться этого можно только с помощью постоянной практики.
На начальных этапах можно пользоваться подсказками и подсматривать в таблицы метасимволов. Обязательно надо обращать внимание на вывод программы, чтобы понимать, какие фрагменты выбираются по шаблонам, и корректировать код. В этом разделе рассмотрим несколько простых задач, которые помогут освоить базовые принципы работы с регулярными выражениями в Python. Постарайтесь придумать свое решение перед тем, как смотреть разбор.
Задача 1
Вернуть первое слово из строки «Регулярные выражения в Python».
Для решения задачи будем использовать функцию re.findall(), которая ищет все вхождения шаблона в любом месте строки. Для начала вернем каждый символ исходной строки с помощью символа точки (.):
import re string = "Регулярные выражения в Python" result = re.findall(r'.', string) print(result) >>> ['Р', 'е', 'г', 'у', 'л', 'я', 'р', 'н', 'ы', 'е', ' ', 'в', 'ы', 'р', 'а', 'ж', 'е', 'н', 'и', 'я', ' ', 'в', ' ', 'P', 'y', 't', 'h', 'o', 'n']
Если обратить внимание на вывод программы, то можно заметить, что в результат попали пробелы. Все из-за того, что символ точки (.) выбирает все символы, кроме новых строк. Исправим это, заменив точку на \w, который выбирает любую букву, цифру или знак нижнего подчеркивания:
import re string = "Регулярные выражения в Python" result = re.findall(r'\w', string) print(result) >>> ['Р', 'е', 'г', 'у', 'л', 'я', 'р', 'н', 'ы', 'е', 'в', 'ы', 'р', 'а', 'ж', 'е', 'н', 'и', 'я', 'в', 'P', 'y', 't', 'h', 'o', 'n']
Мы избавились от пробелов и теперь попробуем объединить буквы в слова с помощью символа +:
import re string = "Регулярные выражения в Python" result = re.findall(r'\w+', string) print(result) >>> ['Регулярные', 'выражения', 'в', 'Python']
Теперь осталось выбрать только первое слово с помощью символа ^:
import re string = "Регулярные выражения в Python" result = re.findall(r'^\w+', string) print(result) >>> ['Регулярные']
Задача 2
Вернуть последнее слово из строки «Регулярные выражения в Python».
Эта задача тесно связана с прошлой. Мы уже научились получать каждое слово из фразы и возвращать только первое. Для того чтобы вернуть последнее слово, следует заменить символ ^ в начале регулярного выражения на $ в конце:
import re string = "Регулярные выражения в Python" result = re.findall(r'\w+$', string) print(result) >>> ['Python']
Задача 3
Проверить формат телефонного номера. Номер должен состоять из 11 знаков и начинаться с 7 или 8. На вход подается список телефонных номеров. Если номер корректный, то следует вернуть Correct, иначе — Incorrect.
Сперва составим регулярное выражение. В самом начале должна идти цифра 7 или 8. Запишем это в виде [78]. После этого следуют 10 любых цифр от 0 до 9 — [0-9]{10}. Если записать это все вместе, то получится [78][0-9]{10}.
Теперь воспользуемся условием if. Если запись номера телефона будет соответствовать шаблону и содержать в себе 11 символов, то будем печатать Correct, иначе — Incorrect. По всему списку номеров пройдемся с помощью цикла for:
import re phone_numbers = ['89154167654', '38764356622', '79853452190', '891678645432', '8916657b589'] for number in phone_numbers: if re.match(r'[78][0-9]{10}', number) and len(number) == 11: print('Correct') else: print('Incorrect') >>> Correct >>> Incorrect >>> Correct >>> Incorrect >>> Incorrect
Задача 4
В офисе сотрудники могут забронировать себе любое рабочее место на целый день. В системе это записывается в виде строки в формате Фамилия_сотрудника Номер_рабочего_места Дата. Номер рабочего места всегда представляет собой трехзначное число, а дата записывается в формате ДД-ММ-ГГГГ. Необходимо написать регулярное выражение, которое вернет даты бронирования рабочих мест.
Для решения задачи надо сперва извлечь все числа из списка. Сделать это можно с помощью \d:
import re booking = 'Иванов 023 14-08-2023, Петров 114 15-08-2023, Семенов 001 20-08-2023, Кутузов 001 01-09-2023' result = re.findall(r'\d', booking) print(result) >>> ['0', '2', '3', '1', '4', '0', '8', '2', '0', '2', '3'...]
На выходе мы получили абсолютно все цифры из списка, но мы знаем точный паттерн, по которому задается дата, — ДД-ММ-ГГГГ. Сперва идут двузначное число дня, потом двузначное число месяца и в самом конце четырехзначное число года. В качестве разделителей используется дефис. Все это можно описать в виде следующего регулярного выражения:
import re booking = 'Иванов 023 14-08-2023, Петров 114 15-08-2023, Семенов 001 20-08-2023, Кутузов 001 01-09-2023' result = re.findall(r'\d{2}-\d{2}-\d{4}', booking) print(result) >>> ['14-08-2023', '15-08-2023', '20-08-2023', '01-09-2023']
Задача 5
В России для регистрации машин используют принятый стандарт регистрационных знаков. Для частных автомобилей он состоит из серии, самого номера и кода региона регистрации. Запись автомобильного номера выглядит так: С065МК777. На вход программы подается список автомобильных номеров. Надо проверить соответствие их записи с государственным стандартом.
Ввод | Вывод |
---|---|
В443РХ777 АА545Р750 Е454АЕ150 М709РО96 В45РАВ77 У822НО02 | ✅ — В443РХ777 ❌ — АА545Р750 ✅ — Е454АЕ150 ✅ — М709РО96 ❌ — В45РАВ77 ✅ — У822НО02 |
Для записи серии номера используются заглавные буквы русского алфавита, у которых есть аналоги в латинице — А, В, Е, К, М, Н, О, Р, С, Т, У и Х. Номер региона записывается двумя или тремя цифрами. Будем проверять наличие последовательности из одной буквы, трех цифр, двух букв и от двух до трех цифр.
По всему списку пройдемся с помощью цикла for, а проверять соответствие и печатать результат будем с помощью условия if:
import re numbers = 'В443РХ777', 'АА545Р750', 'Е454АЕ150', 'М709РО96', 'В45РАВ77', 'У822НО02' for number in numbers: if re.findall(r'[АВЕКМНОРСТУХ]\d{3}[АВЕКМНОРСТУХ]{2}\d{2,3}', number): print('✅ — ' + number) else: print('❌ — ' + number) .
Где практиковаться с регулярными выражениями
Для освоения регулярных выражений нужно много практики. Это поможет запомнить синтаксис и без проблем писать сложные выражения. Для практики можно решать задачи в специальных сервисах:
- Regex Learn — интерактивный курс по регулярным выражениям. Авторы предлагают пошагово изучить тему с нуля. На платформе есть базовый курс из 56 уроков и еще один — по применению регулярных выражений в SEO из 17 уроков из 9 видеороликов. Оба курса бесплатные, но доступны только на английском языке.
- Regex One — еще один пошаговый курс, освещающий все аспекты работы с регулярными выражениями. Во время прохождения придется решать много задач и составлять сложные «регулярки».
- Codewars — на платформе собраны задачи по языкам программирования, но среди них встречаются и регулярные выражения. Можно отфильтровать коллекцию задач по темам, чтобы решать только нужные.
Во время решения задач не всегда удобно запускать полноценную среду разработки для проверки выражения. Для этого есть онлайн-редакторы RegEx, с помощью которых можно вводить «регулярки» в окне браузера. Также на таких платформах есть визуализация работы шаблонов, что может наглядно показать выборку символов. Эта функция особенно полезна на начальных этапах освоения «регулярок».
Список популярных онлайн-редакторов регулярных выражений:
Итог
- Регулярные выражения в Python — мощный инструмент для работы с текстом и строками.
- С помощью регулярных выражений можно проверять валидность адресов, парсить документы и искать данные в тексте.
- Шаблоны позволяют тонко настроить поиск по фрагментам текста.
- В теме регулярных выражений мало теории, и успех освоения технологии зависит от большого количества практики.
- Регулярные выражения поддерживаются практически во всех языках программирования, поэтому, освоив их в Python, можно применять и в других популярных языках.
- Для закрепления материала важно последовательное изучение. Не стоит после решения задач по поиску дат в тексте переходить к парсингу HTML-файлов.