← Часть 15 Методы работы с файлами и файловой системой
Регулярные выражения (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-адреса:
r'^[a-zA-Z0-9._-]+@[a-zA-Z-.]+$'
Этот шаблон – один из простейших, Regex-выражения для проверки email-адресов могут выглядеть гораздо сложнее. Для разработки и тестирования сложных Regex шаблонов используют специальные сервисы, например, Regex101:
regex101 – популярный онлайн конструктор Regex-шаблонов
Основные Regex методы в Python
re.match() – проверяет, начинается ли строка с нужного фрагмента:
import re lst = ['abrakadabra', 'https://kadabra.com', 'https://proglib.io/p/weekly-23-novosti-podkasty-otbornye-stati-i-obuchayushchie-materialy-po-frontendu-2023-02-14 - статья по этой ссылке', 'http//:mysite.ru', 'www.abra.com', 'http//abra.com', 'https://abra.com/', 'это мой сайт - https://abrakadabra.com/', 'https://ru.wikipedia.org/wiki/%D0%9B%D1%8F%D0%B3%D1%83%D1%88%D0%BA%D0%B0-%D0%B3%D0%BE%D0%BB%D0%B8%D0%B0%D1%84'] url = r'https?://(www.)?[-a-zA-Z0-9@:%._+~#=]{1,256}.[a-zA-Z0-9()]{1,6}b([-a-zA-Z0-9()!@:%_+.~#?&/=]*)' for i in lst: m = re.match(url, i) if m: print(m.group(0)) # валидная ссылка извлекается из начала строки
Вывод:
https://kadabra.com https://proglib.io/p/weekly-23-novosti-podkasty-otbornye-stati-i-obuchayushchie-materialy-po-frontendu-2023-02-14 https://abra.com/ https://ru.wikipedia.org/wiki/%D0%9B%D1%8F%D0%B3%D1%83%D1%88%D0%BA%D0%B0-%D0%B3%D0%BE%D0%BB%D0%B8%D0%B0%D1%84
Если нужный фрагмент содержится в тексте, но не в начале строки – re.match() вернет None
:
>>> import re >>> s = 'ой, мороз, мороз, не морозь меня' >>> print(re.match('мороз', s)) None
Метод re.fullmatch() возвращает совпадение, если вся строка полностью соответствует шаблону:
>>> st1, st2 = 'одна строка', 'строка' >>> print(re.fullmatch(r'строка', st1)) None >>> print(re.fullmatch(r'строка', st2)) <re.Match object; span=(0, 6), match='строка'>
Чтобы найти первое вхождение подстроки в текст, используют re.search() , при необходимости – с флагом re.I
для игнорирования регистра:
>>> s = 'Синий, синий иней лег на провода' >>> print(re.search('синий', s, re.I)) <re.Match object; span=(0, 5), match='Синий'>
Метод re.search() можно использовать с дополнительными параметрами span()
, string
и group()
.
span возвращает начальный и конечный индексы вхождения:
>>> text = 'Однажды весною, в час небывало жаркого заката, в Москве, на Патриарших прудах, появились два гражданина.' >>> print(re.search('пруд', text).span()) (71, 75)
string возвращает строку, содержащую искомый фрагмент:
>>> st = 'Дракула Брэма Стокера' >>> print(re.search('Сток', st).string) Дракула Брэма Стокера
group вернет подстроку, совпадающую с запросом:
>>> st = 'пример домашнего хищника: кот' >>> print(' текст найден - ', re.search(r'хищника:swww', st).group()) текст найден - хищника: кот
Все вхождения фрагмента можно найти с помощью re.findall() :
>>> st = 'Eins Hier kommt die Sonne, Zwei Hier kommt die Sonne' >>> print(re.findall('Hier kommt die Sonne', st)) ['Hier kommt die Sonne', 'Hier kommt die Sonne']
Метод re.split() разделяет строку по заданному шаблону:
>>> st = 'мороз и солнце, день чудесный' >>> print(re.split(r'sиs', st, 1)) ['мороз', 'солнце, день чудесный']
Для замены символов и подстрок используют re.sub() . В этом примере регулярное выражение предусматривает удаление из текста всех символов, кроме букв, цифр, знака перевода на новую строку, точки, пробела, вопросительного знака и запятой:
>>> st = 'П#$%^рив&*ет, ка@!к успе~@хи с Py$%^*&thon?' >>> print(re.sub('[^а-яА-Яa-zA-Z0-9,? n.]', '', st)) Привет, как успехи с Python?
Скорость работы скомпилированных Regex-выражений
Компилятор re.compile() применяют в тех случаях, когда шаблон выражения используется повторно:
import re url_lst = ['https://mysite.ru/uploads/2023/2/1/image.jpg', 'https://mysite.ru/uploads/2023/2/1/image.html', 'http://www.mysite.ru/uploads/2022/2/1/another_image.png', 'http://mysite.ru/uploads/2022/12/15/images.doc', 'https://www.mysite.ru/uploads/2022/12/11/image22.jpg', 'http://mysite.ru/images/2023/2/5/gifimage.gif', 'https://mysite.ru/texts/2023/2/1/novel.txt', 'https://mysite.ru/books/2023/2/1/book.epub'] img_url = re.compile(r'https?://(www)?.*.(png|jpg|gif)') for url in url_lst: if img_url.match(url): print(url)
Вывод:
https://mysite.ru/uploads/2023/2/1/image.jpg http://www.mysite.ru/uploads/2022/2/1/another_image.png https://www.mysite.ru/uploads/2022/12/11/image22.jpg http://mysite.ru/images/2023/2/5/gifimage.gif
Как уже упоминалось выше, скомпилированные выражения удобны не только потому, что их можно использовать многократно – они, к тому же, быстрее работают. Чем сложнее выражение и чем больше объем обрабатываемых данных – тем очевиднее преимущество. Проверим, насколько отличается скорость работы обычного регулярного выражения от скомпилированного – возьмем объемный файл («Преступление и наказание» Ф. М. Достоевского) и проведем поиск всех строк, в которых одновременно содержатся имя «Родион» и фамилия «Раскольников» в любых возможных склонениях, при этом между именем и фамилией должно быть не более 5 других слов:
import re import time start = time.time() with open('prestuplenie-i-nakazanie.txt', 'r', encoding='utf8') as book: result = [line for line in book if re.findall(r'bРодиоw{0,3}(?:s+S+){0,5}s+Раскольникоw{0,3}b', line)] print(f'Найдено {len(result)} совпадений. Поиск без компиляции занял {time.time() - start:.2f} секунд') start = time.time() with open('prestuplenie-i-nakazanie.txt', 'r', encoding='utf8') as book: find_name = re.compile(r'bРодиоw{0,3}(?:s+S+){0,5}s+Раскольникоw{0,3}b') result2 = [line for line in book if find_name.findall(line)] print(f'Найдено {len(result2)} совпадений. Поиск с компиляцией занял {time.time() - start:.2f} секунд')
Результат:
Найдено 5 совпадений. Поиск без компиляции занял 0.11 секунд Найдено 5 совпадений. Поиск с компиляцией занял 0.07 секунд
Конструирование регулярных выражений в Python
Regex-шаблоны в Python, как уже упоминалось выше, состоят из метасимволов и литералов, которые определяют символьные комбинации – последовательности, наборы и диапазоны. Регулярные выражения ищут совпадения в обрабатываемом тексте в соответствии с этими комбинациями.
Метасимволы
Рассмотрим основные метасимволы, которые используются для составления Regex-шаблонов.
[]
– определяет набор (или диапазон) символов:
>>> st = 'Роман "Война и мир", автор - Лев Николаевич Толстой' >>> re.findall(r'[о-с]', st, re.I) ['Р', 'о', 'о', 'р', 'о', 'р', 'о', 'о', 'с', 'о'] >>> re.findall(r'[абвлнт]', st, re.I) ['а', 'н', 'В', 'н', 'а', 'а', 'в', 'т', 'Л', 'в', 'Н', 'л', 'а', 'в', 'Т', 'л', 'т']
– задает начало последовательности, а также экранирует служебные символы:
>>> st = 'www.google.com, www.yandex.ru' >>> re.findall(r'.comb', st) ['.com']
.
– позволяет выбрать любой символ, кроме n
:
>>> st = 'Pythonn' >>> re.findall(r'.', st) ['P', 'y', 't', 'h', 'o', 'n']
^
– определяет, начинается ли строка с определенного символа (слова, набора слов или символов). При совместном использовании с []
, напротив, игнорирует набор заданных символов:
>>> st = 'Регулярные выражения в Python' >>> re.match(r'^Р', st) <re.Match object; span=(0, 1), match='Р'> >>> re.findall(r'регулярные^[pyt]', st, re.I) []
$
– определяет, заканчивается ли строка нужным словом, символов или набором символов:
>>> st1, st2 = 'JavaScript', 'Python' >>> print(re.search('pt$', st2)) None >>> re.search('pt$', st1) <re.Match object; span=(8, 10), match='pt'>
.
– соответствует 0 или более символов:
>>> st = 'кооперация, координация, коллаборация' >>> re.findall(r'коо.*', st) ['кооперация, координация, коллаборация']
+
– соответствует 1 и более символов:
>>> st = 'лаборант' >>> re.findall(r'лоб.+', st) [] >>> st = 'лоббирование' >>> re.findall(r'лоб.+', st) ['лоббирование']
?
– обнаруживает наличие 0 или 1 совпадения с шаблоном, а также нейтрализует «жадные» выражения с метасимволами .
, *
, и +
:
>>> st = 'инновационный' >>> re.findall(r'.?нн', st) ['инн', 'онн']
{}
– ищет точное число совпадений, которое указывается в скобках:
>>> st = 'паллиативный, плеоназм, баллистическая, конгрегация, аллопеция' >>> re.findall(r'л{2}', st) ['лл', 'лл', 'лл']
|
– обнаруживает совпадение с любым из указанных вариантов:
>>> st = 'есть карандаши двух цветов - красные и синие' >>> re.findall(r'красные|синие', st) ['красные', 'синие']
()
– выделяет группу символов:
>>> st = 'адреса наших сайтов - www.site1.ru, www.site2.com, www.site3.io' >>> print(re.sub(r'(www.)', r'https://', st)) адреса наших сайтов - https://site1.ru, https://site2.com, https://site3.io
<>
– используется для работы с именованными группами:
>>> info = 'января 12, 2002' >>> pattern = r'^(?P<месяц>w+)s(?P<день>d+),?s(?P<год>d+)' >>> matches = re.search(pattern, info) >>> print(f"Писатель родился в {matches.group('год')} году, {matches.group('день')} {matches.group('месяц')}") Писатель родился в 2002 году, 12 января
Последовательности
Как уже упоминалось выше, знак
обозначает определенную последовательность символов.
A
– проверяет, начинается ли строка с заданной последовательности символов или слов:
>>> text = 'О бойся Бармаглота, сын! Он так свирлеп и дик, А в глуще рымит исполин – Злопастный Брандашмыг.' >>> re.match(r'AО бойся', text) <re.Match object; span=(0, 7), match='О бойся'>
b
– проверяет, 1) начинается ли 2) заканчивается ли слово специфической последовательностью символов:
>>> info = 'www.mysite.com, www.yoursite.com, www.oursite.io' >>> re.findall(r'(www.)b', info) ['www.', 'www.', 'www.'] >>> re.findall(r'(.io)b', info) ['.io']
B
– возвращает совпадение, если указанные символы присутствуют в строке, но 1) не в начале 2) не в конце слов:
>>> st = 'красный, зеленый, нытик' >>> re.findall(r'Bны', st) ['ны', 'ны'] >>> re.findall(r'йB', st) []
d
– определяет, есть ли в строке цифры от 0 до 9:
>>> st = 'собеседование назначено на 12 мая' >>> re.findall(r'd', st) ['1', '2']
D
– соответствует всем символам, кроме цифр:
>>> st = '!#!@#@$%^номер начинается с +7' >>> re.findall(r'D', st) ['!', '#', '!', '@', '#', '@', '$', '%', '^', 'н', 'о', 'м', 'е', 'р', ' ', 'н', 'а', 'ч', 'и', 'н', 'а', 'е', 'т', 'с', 'я', ' ', 'с', ' ', '+']
s
– соответствует одному пробелу:
>>> st = 'один пробел' >>> re.search(r's', st) <re.Match object; span=(4, 5), match=' '>
S
– напротив, соответствует любому символу, кроме пробела:
>>> st = ' ' >>> re.findall(r'S', st) []
w
– соответствует любой букве, цифре или символу _
:
>>> st1, st2 = '!@$^^$%&*()@', 's5tf7_' >>> re.findall(r'w', st1) [] >>> re.findall(r'w', st2) ['s', '5', 't', 'f', '7', '_']
W
– совпадает с любым специальным символом, игнорирует буквы, цифры и _
:
>>> st1, st2 = '!@~#$%^&*(', 'a28df_r4ghgh' >>> re.findall(r'W', st1) ['!', '@', '~', '#', '$', '%', '^', '&', '*', '('] >>> re.findall(r'W', st2) []
Z
– проверяет, заканчивается ли строка нужной последовательностью:
>>> st1, st2 = 'самый популярный язык - Python', 'главный язык интернета - JavaScript' >>> re.search(r'ScriptZ', st2) <re.Match object; span=(29, 35), match='Script'> >>> print(re.search(r'JavaZ', st1)) None
Наборы и диапазоны символов
При составлении регулярных выражений диапазоны и наборы символов заключают в скобки []
. Рассмотрим примеры таких шаблонов.
[абвгд]
, [1234]
, [!@%^]
– находит совпадения с указанными буквами (цифрами, спецсимволами) в строке:
>>> st1, st2 = 'строка без чисел', 'строка с ч1и2с3л4а5м6и' >>> re.findall(r'[сбч]', st1) ['с', 'б', 'ч', 'с'] >>> re.findall(r'[6789]') ['6'] >>> re.findall(r'[!@#$%^]', 'abrakadabr@') ['@']
[а-е]
, [а-еА-Е]
, [5-7]
– находят совпадения с буквами и цифрами из указанных диапазонов:
>>> st1, st2 = 'Мурзилка - советский журнал для детей', 'любимая цифра - 5' >>> re.findall(r'[дежз]', st1) ['з', 'е', 'ж', 'д', 'д', 'е', 'е'] >>> re.findall(r'[м-оМО]', st1) ['М', 'о', 'н'] >>> re.findall(r'[4-7]', st2) ['5']
[^абв]
, [^123]
, [^!@#$]
– совпадает с любым символом, не входящим в указанный набор (диапазон):
>>> st = 'пример строки с ц1и2ф3рами и с!мвол@ми' >>> re.findall(r'[^3-5]', st) ['п', 'р', 'и', 'м', 'е', 'р', ' ', 'с', 'т', 'р', 'о', 'к', 'и', ' ', 'с', ' ', 'ц', '1', 'и', '2', 'ф', 'р', 'а', 'м', 'и', ' ', 'и', ' ', 'с', '!', 'м', 'в', 'о', 'л', '@', 'м', 'и'] >>> re.findall(r'[^а-о]', st) ['п', 'р', 'р', ' ', 'с', 'т', 'р', ' ', 'с', ' ', 'ц', '1', '2', 'ф', '3', 'р', ' ', ' ', 'с', '!', '@'] >>> re.findall(r'[^#$%^&*()_+]', st) ['п', 'р', 'и', 'м', 'е', 'р', ' ', 'с', 'т', 'р', 'о', 'к', 'и', ' ', 'с', ' ', 'ц', '1', 'и', '2', 'ф', '3', 'р', 'а', 'м', 'и', ' ', 'и', ' ', 'с', '!', 'м', 'в', 'о', 'л', '@', 'м', 'и']
[0-9][0-9]
– позволяет задавать совпадения по двузначным цифрам:
>>> st = 'встреча назначена на 10:45' >>> re.findall(r'[0-9][0-9]', st) ['10', '45']
Флаги в регулярных выражениях
В Python предусмотрены дополнительные параметры для Regex-шаблонов – флаги , причем использовать можно и полную, и краткую форму параметра. Ранее мы уже встречались с флагом re.I
– это краткий вариант re.IGNORECASE
. На практике флаг re.I
используется чаще всего, но остальные флаги тоже могут пригодиться.
re.I
, re.IGNORECASE
– игнорирует регистр:
>>> st = 'Яблоко от яблони недалеко падает' >>> re.findall('ябл', st, re.I) ['Ябл', 'ябл']
re.A
, re.ASCII
– находит ASCII -символы, игнорируя все остальные:
st = 'одно из слов для обозначения дракона в японском - ドラゴン, doragon' >>> re.findall(r'w+', st, re.A) ['doragon']
re.M
, re.MULTILINE
– находит совпадения в начале ^
и конце $
каждой строки в многострочном фрагменте текста:
>>> st = 'Это пример текста,n состоящего изn нескольких строкn' >>> print(re.search(r'^sсостоящего', st)) None >>> print(re.search(r'^sсостоящего', st, re.M)) <re.Match object; span=(19, 30), match=' состоящего'>
re.S
, re.DOTALL
– позволяет метасимволу .
возвращать совпадения по всем символам, включая n
:
>>> st = 'примерn строкиn nс nсимволом "n"' >>> re.findall('.', st) ['п', 'р', 'и', 'м', 'е', 'р', ' ', 'с', 'т', 'р', 'о', 'к', 'и', ' ', 'с', ' ', 'с', 'и', 'м', 'в', 'о', 'л', 'о', 'м', ' ', '"', '"'] >>> re.findall('.', st, re.S) ['п', 'р', 'и', 'м', 'е', 'р', 'n', ' ', 'с', 'т', 'р', 'о', 'к', 'и', 'n', ' ', 'n', 'с', ' ', 'n', 'с', 'и', 'м', 'в', 'о', 'л', 'о', 'м', ' ', '"', 'n', '"']
re.X
, re.VERBOSE
– позволяет использовать комментарии в Regex-шаблонах:
import re pattern = re.compile(r''' ^[a-zA-Z0-9._-]+ # первая часть адреса содержит буквы, цифры, подчеркивание и дефис @ # первая часть адреса соединяется со второй символом @ [a-zA-Z.-]+$ # заключительная часть - доменное имя (может содержать дефис) и доменная зона ''', re.X) emails = ['python4ik@python.org', '@4##@%@mail.ru', 'python@yandex.ru', 'my_em@il@mail.com'] for email in emails: if pattern.fullmatch(email): print(email)
Вывод:
python4ik@python.org python@yandex.ru
Опережающие и ретроспективные проверки
Как и большинство других современных языков программирования, Python поддерживает опережающие и ретроспективные проверки – позитивные и негативные.
Позитивная опережающая проверка:
X(?=Y) – вернуть X только в том случае, если выполняется Y .
Негативная опережающая проверка:
X(?!Y) – вернуть X только в том случае, если не выполняется Y .
Ретроспективная позитивная проверка:
(?<=Y)X – вернуть X при условии, что перед ним есть Y .
Ретроспективная негативная проверка:
(?<!Y)X – вернуть совпадение с X при условии, что перед ним нет Y .
Вот пример позитивной опережающей проверки – здесь Regex-шаблон находит в тексте слова, которые 1) имеют длину ровно 10 символов; 2) включают в себя подстроку «кофе»:
import re pattern = re.compile(r'(?=bw{10}b)w*?кофеw*', re.I) text = '''В кофейне появились новые кофемашина и кофемолка. Кофемашина делает все виды кофеиносодержащих и некофейных напитков (чай и какао).''' print(pattern.findall(text))
Вывод:
['кофемашина', 'Кофемашина', 'некофейных']
А это пример негативной опережающей проверки – шаблон находит совпадения только по тем цифрам, после которых нет «руб»:
>>> import re >>> st = '1 месяц хостинга стоит 200 руб' >>> re.findall(r'd+(?!sруб)b', st) ['1']
Ретроспективная позитивная проверка здесь находит числа, перед которыми стоит «рублей»:
>>> st = 'VPN на 2 месяца стоит рублей 300' >>> re.findall(r'(?<=рублейs)d+', st) ['300']
А здесь ретроспективная негативная проверка находит числа, перед которыми нет «примерно»:
>>> st = 'Стоимость 2 приличных ноутбуков - примерно 4 тысячи долларов' >>> re.findall(r'(?<!примерноs)d+', st) ['2']
Практика
Задание 1
Напишите регулярное выражения, которое находит в полученной от пользователя строке все слова с дефисом.
Пример ввода:
Почему-то часто никак как-то получилось что-то зачем-то опять Кто-то
Вывод:
['Почему-то', 'как-то', 'что-то', 'зачем-то', 'Кто-то']
Решение с Regex:
import re st = input() print(re.findall(r'b[а-я]+-[а-я]+b', st, re.I))
Решение без Regex:
st = input().lower().split() print([i for i in st if '-' in i])
Задание 2
Напишите программу, которая с помощью Regex-шаблона определяет, сколько слов в полученной от пользователя строке начинаются с «ко» или «коо».
Пример ввода:
Книга компьютер крот Колобок колхоз кооперация ноутбук карандаш координатор
Вывод:
5
Решение с Regex:
import re st = input() print(len(re.findall(r'ко{1,2}', st, re.I)))
Решение без Regex:
st = input().lower().split() print(len([i for i in st if i.startswith('ко') or i.startswith('коо')]))
Задание 3
Напишите регулярное выражение, которое удаляет из текста все знаки препинания, кроме дефиса.
Пример ввода:
"Это" - фрагмент текста, для обработки?!..
Вывод:
Это - фрагмент текста для обработки
Решение с Regex:
import re st = input() print(re.sub(r'[,.?!:;"]', '', st))
Решение без Regex:
st = input() print(''.join([i for i in st if i.isalpha() or i == ' ' or i == '-']))
Задание 4
Напишите регулярное выражение, которое находит в полученной от пользователя строке все слова, содержащие подстроку «круж», но не в начале и не в конце слова.
Пример ввода:
окружность кружево кружка окружение головокружение кружок кружкруж
Вывод:
окружность окружение головокружение
Решение с Regex:
import re st = input().split() for i in st: if re.search(r'BкружB', i): print(i)
Решение без Regex:
st = input().split() for i in st: if 'круж' in i: if i.startswith('круж'): continue elif i.endswith('круж'): continue else: print(i)
Задание 5
Напишите регулярное выражение, которое меняет формат даты в URL с ГГГГ/ММ/ДД на ДД/ММ/ГГГГ .
Пример ввода:
https://www.washingtonpost.com/technology/2023/02/14/what-is-temu-super-bowl-commercial/
Вывод:
https://www.washingtonpost.com/technology/14/02/2023/what-is-temu-super-bowl-commercial/
Решение:
import re url = input() print(re.sub(r'(d{4})/(d{1,2})/(d{1,2})', r'3/2/1', url))
Задание 6
Напишите программу, которая:
Получает от пользователя n строк с данными студентов.
Извлекает имена, фамилии и оценки по предметам без использования методов строк и словарей.
Создает и выводит список словарей.
Пример ввода:
5 Денис,Ефремов,5,5,3,4 Юлия,Демидова,5,3,4,5 Евгения,Артемова,4,4,4,5 Сергей,Егоров,4,4,4,3 Кирилл,Антонов,4,5,3,3
Вывод:
[{'имя': 'Денис', 'фамилия': 'Ефремов', 'математика': '5', 'физика': '5', 'химия': '3', 'биология': '4'}, {'имя': 'Юлия', 'фамилия': 'Демидова', 'математика': '5', 'физика': '3', 'химия': '4', 'биология': '5'}, {'имя': 'Евгения', 'фамилия': 'Артемова', 'математика': '4', 'физика': '4', 'химия': '4', 'биология': '5'}, {'имя': 'Сергей', 'фамилия': 'Егоров', 'математика': '4', 'физика': '4', 'химия': '4', 'биология': '3'}, {'имя': 'Кирилл', 'фамилия': 'Антонов', 'математика': '4', 'физика': '5', 'химия': '3', 'биология': '3'}]
Решение:
import re pattern = re.compile(r'''(?P<имя>[^,]+), # именованная группа 1 (?P<фамилия>[^,]+), # именованная группа 2 и так далее (?P<математика>[^,]+), (?P<физика>[^,]+), (?P<химия>[^,]+), (?P<биология>[^,]+) ''', re.X) grades = [] for i in range(int(input())): line = input() grades.append(pattern.search(line).groupdict()) print(grades)
Задание 7
Напишите регулярные выражения, которые:
Заменяют все вхождения слова «красный» на «зеленый», но только в том случае, если перед словом «красный» нет союза «и».
Находят все слова, которые не заканчиваются на «и» или «ый».
Находят все слова, которые не начинаются с букв «к», «ф», «о» и имеют длину 2 и более символов.
Пример текста:
st = '''красноватый фиолетовый и красный красный и желтый красный желтый и красный красный и оранжевый прекрасный окрас розоватый и красный краснота'''
Вывод:
красноватый фиолетовый и красный зеленый и желтый зеленый желтый и красный зеленый и оранжевый прекрасный окрас розоватый и красный краснота окрас краснота желтый желтый прекрасный розоватый
Решение:
import re st = '''красноватый фиолетовый и красный красный и желтый красный желтый и красный красный и оранжевый прекрасный окрас розоватый и красный краснота''' print(re.sub(r'(?<!иs)bкрасныйb', 'зеленый', st), 'n') print(*re.findall(r'bw+b(?<![иый])', st), 'n') print(*re.findall(r'(?![кфо])bw{2,}', st))
Задание 8
Напишите регулярные выражения, которые:
Удаляют все html-теги из полученной от пользователя строки.
Вставляют пробелы перед заглавными буквами в тексте и ставят точки в конце предложений.
Пример ввода:
<h1>Это заголовок первого уровня</h1><p>Это текст параграфа<strong>Это важный текст внутри параграфа</strong></p><p>Это второй параграф</p>
Вывод:
Это заголовок первого уровня. Это текст параграфа. Это важный текст внутри параграфа. Это второй параграф.
Решение:
import re st = input() st = re.sub('<[^<>]+>', '', st) print(re.sub(r'(w)([А-Я]|$)', r'1. 2', st))
Задание 9
Напишите регулярное выражение для валидации пароля. Надежный пароль имеет длину от 8 до 20 символов и включает в себя хотя бы:
один символ в верхнем регистре;
один символ в нижнем регистре;
одну цифру;
один спецсимвол из набора @$!%*#?&
.
Пример ввода 1:
cheBur@sh!ka
Вывод:
Ненадежный пароль
Пример ввода 2:
cheBur@sh#ka5
Вывод:
Надежный пароль
Решение:
import re valid = re.compile(r'^(?=.*[a-z])(?=.*[A-Z])(?=.*d)(?=.*[@$!%*#?&])[A-Za-zd@$!#%*?&]{8,20}$') passw = input() print('Надежный пароль' if valid.fullmatch(passw) else 'Ненадежный пароль')
Задание 10
Напишите программу, которая получает от пользователя строку с IP-адресом и определяет, является ли этот адрес:
корректным IPv4-адресом;
корректным IPv6-адресом;
адресом некорректного формата.
Корректный IPv4-адрес соответствует формату x1.x2.x3.x4 , где 0 <= xi <= 255 , и не содержит ведущих нулей. В корректный IPv6 адрес, состоящий из 128 битов, входят восемь групп из четырех шестнадцатеричных цифр; группы разделены двоеточиями.
Пример ввода 1:
192.168.1.0
Вывод:
Корректный IPv4
Пример ввода 2:
2001:0db8:85a3:0000:0000:8a2e:0370:7334
Вывод:
Корректный IPv6
Пример ввода 3:
192.168.1.000
Вывод:
Адрес имеет некорректный формат
Решение:
import re valid_ip4 = re.compile(r'(([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5]).){3}([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])') valid_ip6 = re.compile(r'((([0-9a-fA-F]){1,4}):){7}([0-9a-fA-F]){1,4}') ip = input() if valid_ip4.fullmatch(ip): print('Корректный IPv4') elif valid_ip6.fullmatch(ip): print('Корректный IPv6') else: print('Адрес имеет некорректный формат')
Подведем итоги
Regex – мощный, но достаточно сложный инструмент: для конструирования и тестирования шаблонов лучше пользоваться специальными сервисами, которые помогают визуализировать результат работы выражения. Во многих случаях регулярные выражения можно заменить методами строк или специальным html/xml/ txt парсером.
В следующей статье будем изучать основы скрапинга и парсинга.
Содержание самоучителя
Особенности, сферы применения, установка, онлайн IDE
Все, что нужно для изучения Python с нуля – книги, сайты, каналы и курсы
Типы данных: преобразование и базовые операции
Методы работы со строками
Методы работы со списками и списковыми включениями
Методы работы со словарями и генераторами словарей
Методы работы с кортежами
Методы работы со множествами
Особенности цикла for
Условный цикл while
Функции с позиционными и именованными аргументами
Анонимные функции
Рекурсивные функции
Функции высшего порядка, замыкания и декораторы
Методы работы с файлами и файловой системой
Регулярные выражения
***
Шпаргалка по регулярным выражениям
Скачать в формате .pdf ***
Материалы по теме
🐍 Регулярные выражения в Python за 5 минут: теория и практика для новичков и не только
Больше полезных материалов вы найдете на нашем телеграм-канале «Библиотека питониста» Интересно, перейти к каналу