🐍 Самоучитель по Python для начинающих. Часть 16: Регулярные выражения
Рассмотрим встроенные функции модуля re, научимся компилировать Regex-выражения и узнаем, как делать опережающие и ретроспективные проверки – позитивные и негативные. В конце статьи, как всегда, – 10 интересных заданий с решениями.
Регулярные выражения (Regex) – это особые шаблоны для поиска
определенных подстрок в текстовых документах и на веб-страницах. Концепция Regex появилась в 1951 году, стала
популярной к 1968 году, и с тех пор в той или иной степени поддерживается в
большинстве языков программирования общего назначения. Регулярные выражения
используютсяв текстовых редакторах, в
файловых менеджерах ОС, в OCR-приложениях
для распознавания текста, в онлайн-поисковиках и браузерах. Кроме того, они применяются
для:
валидации данных;
лексического анализа;
определения директив конфигурации и преобразования URL (Apache http.conf, mod_rewrite);
составления сложных SQL-запросов;
создания кастомных шаблонов URL-диспетчера (re_path() Django).
Регулярные выражения в Python
Для работы с Regex в Python используют встроенный модуль re, в который входят:
Набор функций для поиска и замены подстрок – ниже мы подробно рассмотрим примеры использования основных методов.
Компилятор re.compile – он создает Regex-объекты для повторного использования и ускоряет работу регулярных выражений, как мы увидим чуть позже.
Регулярные выражения состоят из литералов (букв и цифр) и метасимволов. Для экранирования спецсимволов применяют обратные слэши \, или же заключают выражение в r-строку . Такой шаблон, к примеру, можно использовать для валидации email-адреса:
Этот шаблон – один из простейших, Regex-выражения для проверки email-адресов могут выглядеть гораздо сложнее. Для разработки и тестирования сложных Regex шаблонов используют специальные сервисы, например, Regex101:
Основные Regex методы в Python
re.match() – проверяет,
начинается ли строка с нужного фрагмента:
Вывод:
Если нужный фрагмент содержится в тексте, но не в начале строки – re.match() вернет None:
Методre.fullmatch() возвращает
совпадение, если вся строка полностью соответствует шаблону:
Чтобы найти первое вхождение подстроки в текст, используют re.search(), при необходимости – с флагом re.I для игнорирования регистра:
Метод re.search() можно использовать с
дополнительными параметрами span(),
stringи group().
span возвращает начальный и конечный индексы вхождения:
Все вхождения фрагмента можно найти с помощью re.findall():
Метод re.split() разделяет строку по заданному шаблону:
Для замены символов и подстрок используют re.sub(). В этом примере регулярное выражение предусматривает удаление из текста всех символов, кроме букв, цифр, знака перевода на новую строку, точки, пробела, вопросительного знака и запятой:
Скорость работы скомпилированных Regex-выражений
Компилятор re.compile() применяют в тех
случаях, когда шаблон выражения используется повторно:
Вывод:
Как уже упоминалось выше, скомпилированные выражения удобны не только потому, что их можно использовать многократно – они, к тому же, быстрее работают. Чем сложнее выражение и чем больше объем обрабатываемых данных – тем очевиднее преимущество. Проверим, насколько отличается скорость работы обычного регулярного выражения от скомпилированного – возьмем объемный файл («Преступление и наказание» Ф. М. Достоевского) и проведем поиск всех строк, в которых одновременно содержатся имя «Родион» и фамилия «Раскольников» в любых возможных склонениях, при этом между именем и фамилией должно быть не более 5 других слов:
Результат:
Конструирование регулярных выражений в Python
Regex-шаблоны в Python, как уже упоминалось выше, состоят из метасимволов и литералов, которые определяют символьные комбинации – последовательности, наборы и диапазоны. Регулярные выражения ищут совпадения в обрабатываемом тексте в соответствии с этими комбинациями.
Метасимволы
Рассмотрим основные метасимволы, которые используются для
составления Regex-шаблонов.
[] – определяет набор (или диапазон) символов:
\ – задает начало последовательности, а также экранирует служебные символы:
. – соответствует любому символу, кроме \n:
^ – определяет, начинается ли строка с определенного символа (слова, набора слов или символов). При совместном использовании с [], напротив, игнорирует набор заданных символов:
$ – определяет, заканчивается ли строка нужным словом, символов или набором символов:
.* – используется для поиска любого количества любых символов, кроме \n:
.+ – соответствует любой строке, которая содержит хотя бы один символ, кроме \n:
? – обнаруживает наличие 0 или 1 совпадения с шаблоном, а также нейтрализует «жадные» выражения с метасимволами ., *, и +:
{} – ищет точное число совпадений, которое указывается в скобках:
| – обнаруживает совпадение с любым из указанных вариантов:
() – выделяет группу символов:
<> – используется для работы с именованными группами:
Последовательности
Как уже упоминалось выше, знак \ обозначает определенную
последовательность символов.
\A – проверяет, начинается ли строка с заданной
последовательности символов или слов:
\b – проверяет, 1) начинается ли 2) заканчивается ли слово специфической последовательностью символов:
\B – возвращает совпадение, если указанные символы присутствуют в строке, но 1) не в начале 2) не в конце слов:
\d – определяет, есть ли в строке цифры от 0 до 9:
\D – соответствует всем символам, кроме цифр:
\s – соответствует одному пробелу:
\S – напротив, соответствует любому символу, кроме пробела:
\w – соответствует любой букве, цифре или символу _:
\W – совпадает с любым специальным символом, игнорирует буквы, цифры и _:
\Z – проверяет, заканчивается ли строка нужной последовательностью:
Наборы и диапазоны символов
При составлении регулярных выражений диапазоны и наборы
символов заключают в скобки []. Рассмотрим примеры таких шаблонов.
[абвгд], [1234], [!@%^] – находит совпадения с указанными
буквами (цифрами, спецсимволами) в строке:
[а-е], [а-еА-Е], [5-7] – находят совпадения с буквами и цифрами из указанных диапазонов:
[^абв], [^123], [^!@#$] – совпадает с любым символом, не входящим в указанный набор (диапазон):
[0-9][0-9] – позволяет задавать совпадения по двузначным цифрам:
Флаги в регулярных выражениях
В Pythonпредусмотрены дополнительные параметры для Regex-шаблонов – флаги, причем
использовать можно и полную, и краткую форму параметра. Ранее мы уже
встречались с флагом re.I–
это краткий вариант re.IGNORECASE. На практике флаг re.Iиспользуется
чаще всего, но остальные флаги тоже могут пригодиться.
re.I,re.IGNORECASE
– игнорирует регистр:
re.A, re.ASCII – находит ASCII-символы, игнорируя все остальные:
re.M, re.MULTILINE – находит совпадения в начале ^ и конце $ каждой строки в многострочном фрагменте текста:
re.S, re.DOTALL – позволяет метасимволу . возвращать совпадения по всем символам, включая \n:
re.X, re.VERBOSE – позволяет использовать комментарии в Regex-шаблонах:
Вывод:
Опережающие и ретроспективные проверки
Как и большинство других современных языков
программирования, Python
поддерживает опережающие и ретроспективные проверки – позитивные и негативные.
Позитивная опережающая проверка:
X(?=Y) – вернуть X только в том случае, если выполняется Y.
Негативная опережающая проверка:
X(?!Y) – вернуть X только в том случае, если не выполняется
Y.
Ретроспективная позитивная проверка:
(?<=Y)X – вернуть X при условии, что перед ним естьY.
Ретроспективная негативная проверка:
(?<!Y)X – вернуть совпадение с X при условии, что перед
ним нетY.
Вот пример позитивной опережающей проверки – здесь Regex-шаблон находит в тексте
слова, которые 1) имеют длину ровно 10 символов; 2) включают в себя подстроку
«кофе»:
Вывод:
А это пример негативной опережающей проверки – шаблон
находит совпадения только по тем цифрам, после которых нет «руб»:
Ретроспективная позитивная проверка здесь находит числа, перед которыми стоит «рублей»:
А здесь ретроспективная негативная проверка находит числа,
перед которыми нет «примерно»:
Практика
Задание 1
Напишите регулярное выражения, которое находит в полученной
от пользователя строке все слова с дефисом.
Пример ввода:
Вывод:
Решение с Regex:
Решение без Regex:
Задание 2
Напишите программу, которая с помощью Regex-шаблона определяет, сколько слов в
полученной от пользователя строке начинаются с «ко» или «коо».
Пример ввода:
Вывод:
Решение с Regex:
Решение без Regex:
Задание 3
Напишите регулярное выражение, которое удаляет из текста все
знаки препинания, кроме дефиса.
Пример ввода:
Вывод:
Решение с Regex:
Решение без Regex:
Задание 4
Напишите регулярное выражение, которое находит в полученной
от пользователя строке все слова, содержащие подстроку «круж», но не в начале и
не в конце слова.
Пример ввода:
Вывод:
Решение с Regex:
Решение без Regex:
Задание 5
Напишите регулярное выражение, которое меняет формат даты в URLс
ГГГГ/ММ/ДД на ДД/ММ/ГГГГ.
Пример ввода:
Вывод:
Решение:
Задание 6
Напишите программу, которая:
Получает от пользователя n строк с данными студентов.
Извлекает имена, фамилии и оценки по предметам без использования методов строк и словарей.
Создает и выводит список словарей.
Пример ввода:
Вывод:
Решение:
Задание 7
Напишите регулярные выражения, которые:
Заменяют все вхождения слова «красный» на «зеленый», но только в том случае, если перед словом «красный» нет союза «и».
Находят все слова, которые не заканчиваются на «и» или «ый».
Находят все слова, которые не начинаются с букв «к», «ф», «о» и имеют длину 2и более символов.
Пример текста:
Вывод:
Решение:
Задание 8
Напишите регулярные выражения, которые:
Удаляют все html-теги из полученной от пользователя строки.
Вставляют пробелы перед заглавными буквами в тексте и ставят точки в конце предложений.
Пример ввода:
Вывод:
Решение:
Задание 9
Напишите регулярное выражение для валидации пароля.
Надежный пароль имеет длину от 8 до 20 символов и включает в себя хотя бы:
один символ в верхнем регистре;
один символ в нижнем регистре;
одну цифру;
один спецсимвол из набора @$!%*#?&.
Пример ввода 1:
Вывод:
Пример ввода 2:
Вывод:
Решение:
Задание 10
Напишите программу, которая получает от пользователя строку
с IP-адресом и
определяет, является ли этот адрес:
корректным IPv4-адресом;
корректным IPv6-адресом;
адресом некорректного формата.
Корректный IPv4-адрессоответствует формату x1.x2.x3.x4, где 0
<= xi <= 255, и не содержит ведущих нулей. В корректный IPv6 адрес,
состоящий из 128 битов, входят восемь групп из четырех шестнадцатеричных цифр;
группы разделены двоеточиями.
Пример ввода 1:
Вывод:
Пример ввода 2:
Вывод:
Пример ввода 3:
Вывод:
Решение:
Подведем итоги
Regex
– мощный, но достаточно сложный инструмент: для конструирования и тестирования
шаблонов лучше пользоваться специальными сервисами, которые помогают
визуализировать результат работы выражения. Во многих случаях регулярные
выражения можно заменить методами строк или специальным html/xml/ txtпарсером.
В следующей статье будем изучать основы скрапинга и
парсинга.
Комментарии