Наталья Кайда 05 апреля 2022

🐍 Регулярные выражения в Python за 5 минут: теория и практика для новичков и не только

Учимся использовать Regex: немного теории, примеры выражений и 10 практических заданий для отработки навыков.
🐍 Регулярные выражения в Python за 5 минут: теория и практика для новичков и не только

Что такое Regex

Регулярные выражения (Regex) – это строки, задающие шаблон для поиска определенных фрагментов в тексте. Помимо поиска, с помощью специальных Regex-шаблонов можно манипулировать текстовыми фрагментами – удалять и изменять подстроки частично или полностью.

Регулярные выражения состоят из набора литералов (букв и цифр) и метасимволов и выглядят примерно так: r'(https?://)?(www\.)?youtube\.(com|nl)/watch\?v=([\w-]+)(&.*?)?(?=[^-\w&=%])'Используя метасимволы, можно создавать сложные шаблоны, содержащие специальные конструкции для работы с определенными последовательностями и группами символов.

Regex-выражения применяют для обработки текстовых данных, в том числе в скриптах для веб-скрапинга. Кроме того, Regex используют в составе OCR-приложений для очистки отсканированного текста.

Regex в Python

Большинство современных языков программирования поддерживают регулярные выражения, однако степень удобства использования Regex в разных языках варьируется. Python предоставляет простые и понятные методы для работы с регулярными выражениями. Все Regex инструменты находятся в модуле re, который входит в стандартный дистрибутив Python – достаточно импортировать его в свой проект:

import re

Для экранирования служебных символов в шаблонах поиска и замены используют два способа – обратный слэш \ и «сырые» строки r''. Второй метод предпочтительнее – он позволяет избежать нагромождения слэшей в шаблонах.

Основные функции Regex

re.match() – находит вхождение фрагмента в начале строки. Обычный формат использования – re.match(r'шаблон', строка):

        import re
s = "утка крякает, кукушка кукует, петух кукарекает"
match = re.match(r'ку', s)
print(match)

    

Этот код вернет None, несмотря на то, что в строке есть 5 фрагментов «ку». Это происходит потому, что оба фрагмента расположены не в начале строки.

re.search() – находит первое вхождение фрагмента в любом месте и возвращает объект match. Если в строке есть другие фрагменты, соответствующие запросу, re.search их проигнорирует. У re.search есть дополнительные методы:

.span() – возвращает кортеж, содержащий начальную и конечную позиции искомого фрагмента.

.string – вернет строку, переданную в функцию re.search.

.group() – возвращает фрагмент строки, в котором было обнаружено совпадение.

        #Пример использования re.search с дополнительными методами
import re
s = "oт топота копыт пыль по полю летит"
match = re.search(r'по', s)
print(match, match.span(), match.string, match.group(), sep='\n')

    
        #Вывод:
<re.Match object; span=(5, 7), match='по'>
(5, 7)
oт топота копыт пыль по полю летит
по

    

re.findall() – находит все вхождения фрагмента, в любом месте. Функция re.findall() учитывает регистр символов. Чтобы в результат вошли фрагменты с символами в другом регистре, применяют флаг re.IGNORECASE:

        import re
s = "Не видно, ликвидны акции или неликвидны."
match = re.findall(r'не', s, re.I)
print(match)

    

re.split() – расщепляет строку по заданному шаблону. Количество расщеплений задается флагом – в этом примере от строки отделяется только первое слово:

        import re
s = "Обладаешь ли ты налогооблагаемой благодатью?"
res = re.split(r' ', s, 1)
print(res) 

    

re.sub() – заменяет фрагмент в соответствии с шаблоном:

        import re
s = "Коала какао лениво лакала"
res = re.sub(r'коала', 'макака', s, flags=re.I)
print(res)

    

re.compile() – создает объект из регулярного выражения. Применяется, если один и тот же поисковый шаблон используется в коде несколько раз:

        import re
st = re.compile('угнал')
res1 = st.findall("Карл у Клары угнал Maclaren, а Клара у Карла угнала Corvette.")
res2 = st.findall("Карл у Клары угнал кораллы, а Клара у Карла угнала кларнет.")
print(res1, res2, sep='\n')

    

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

Больше полезных материалов вы найдете на нашем телеграм-канале «Библиотека питониста»

Основные метасимволы в Regex

[] – используется для указания набора или диапазона символов – re.findall(r'[с-я]', "Камер-юнкер юркнул в бункер", re.I), re.findall(r'[аж]', "ажиотаж, мандраж, багаж").

\ – указывает на начало последовательности (мы рассмотрим их ниже) или экранирует служебные символы.

. – выбирает любой символ, кроме новой строки \n.

^ – проверяет, начинается ли строка с определенного символа / слова / набора символов. Например, r'^Привет' проверит, начинается ли строка с «Привет». Метасимвол ^ в наборе [] имеет другое значение – проверяет, отсутствуют ли в строке определенные символы (подробнее об этом ниже).

$ – проверяет, заканчивается ли строка в соответствии с шаблоном r'До свиданья.$'.

* – ноль или больше совпадений с шаблоном r'ко.*аборация'.

+ – одно и более совпадений r'к.+ператив'.

? – ноль или одно совпадение r'ф.?нтастика'. Кроме того, нейтрализует «жадность» выражений, которые используют ., *, + для выбора любых символов.

{} – точное число совпадений r'Интерсте.{2}ар'.

| – любой из двух вариантов r'уйду|останусь'.

() – захватывает группу для дальнейших манипуляций – re.sub(r'(www)', r'\1.', "wwwwear-gear.com").

<> – создает именованную группу – re.search('(?P<группа1>\w+),(?P<группа2>\w+),(?P<группа3>\w+)', 'дом,улица,фонарь')

Последовательности

Знаком слэша \ обозначается специфическая последовательность символов.

\A – проверяет, начинается ли строка с определенной последовательности символов. Например, re.findall(r"\AДом", txt), проверит, начинается ли предложение со слова «Дом».

\b – возвращает совпадение, если слово начинается или заканчивается нужной последовательностью символов. Выражение re.findall(r".com\b", s) проверит, есть ли в строке хотя бы одно доменное имя зоны .com.

\B – возвращает совпадение, если определенные символы есть в строке, но не в начале или не в конце слова – re.findall(r"\Bро", 'розовая от мороза'), re.findall(r'ин\B', 'синий апельсин').

\d – проверяет, что в строке есть цифры от 0 до 9 – re.findall("\d", 'при пожаре звоните 112').

\D – удостоверяет, что цифр в строке нет – re.findall("\D", 'цифр нет').

\s – проверяет наличие пробелов в строке – re.findall("\s", "один пробел").

\S – возвращает совпадение, если в строке есть любые символы, кроме пробелов – re.findall("\S", "непустая строка").

\w – проверяет, есть ли в строке «словесные» символы – знак нижнего подчеркивания, цифры и буквы – re.findall(r"\w", "_\\\\").

\W – возвращает совпадение по каждому «несловесному» символу – re.findall("\W", "здесь есть такие символы!").

\Z – проверит, заканчивается ли строка нужной последовательностью символов – re.findall("конец\Z", "это конец").

Наборы и диапазоны символов

Наборы и диапазоны в регулярных выражениях заключены в квадратные скобки:

[есн] – проверит, есть ли в строке любой из указанных символов е, с или нre.findall("[есн]", "здесь есть несколько символов из набора"). Наличие любой цифры из набора проверяется так же – [0169].

[а-е] – вернет совпадения по каждому символу из алфавитного диапазона – re.findall("[а-е]", "здесь есть символы из диапазона"). Таким же образом возвращает совпадения по диапазону цифр – [5-9]. Чтобы не использовать флаг re.IGNORECASE, диапазон можно указывать так – [а-еА-Е].

[^абвгд] – проверит наличие в строке символов, кроме указанных в наборе – re.findall("[^абвгд]", "АБВГДейка – детская передача", re.I).

[0-5][0-9] – возвращает совпадения по двузначным цифрам от 00 до 59re.findall("[0-5][0-9]", "будильник сработает в 07:45").

Флаги в Regex

Функциональность регулярных выражений расширяется за счет флагов:

Краткий синтаксис Полный синтаксис Назначение
re.A re.ASCII Возвращает совпадения только по ASCII-символам вместо всей таблицы Unicode.
re.I re.IGNORECASE Игнорирует регистр символов.
re.M re.MULTILINE Используется совместно с метасимволами ^ и $. В первом случае возвращает совпадения в начале каждой новой строки \n, во втором – в конце \n.
re.S re.DOTALL Заставляет метасимвол . возвращать совпадения по абсолютно всем символам, включая \n. Без этого флага точка . соответствует любому символу, кроме \n.
re.X re.VERBOSE Разрешает комментарии в Regex-выражениях.
re.L re.LOCALE Учитывает региональные настройки при использовании \w, \W, \b, \B, \s и \S. Используется только при работе с байтовыми строками, не совместим с re.ASCII.

Онлайн-конструкторы регулярных выражений

Чем сложнее регулярное выражение, тем труднее его правильно составить и протестировать. В интернете есть немало визуализаторов Regex, которые значительно упрощают эту задачу. Самый удобный ресурс – regex101. Сайт предоставляет справочную и отладочную информацию, позволяет визуально тестировать шаблоны для поиска и замены. Помимо Python, поддерживает PHP, Java, Golang и JavaScript.

<i>Конструктор Regex</i>
Конструктор Regex

Примеры использования регулярных выражений в Python

Задача 1:

Написать регулярное выражение для извлечения из текста всех email-адресов.

Решение:

        import re
s = 'По всем вопросам пишите на vasiliy-pupkin@gmail.com, или на secondemail@yandex.ru, отвечу сразу. Или пишите моему ассистенту secretary@gmail.com!'
emails = re.findall(r'[\w\.-]+@[\w\.-]+', s)
for email in emails:
    print(email)

    

Задача 2:

Имеется файл transactions.txt, в котором даты указаны в формате MM/DD/YYYY, при этом в некоторых случаях месяц обозначен первыми тремя буквами: NOV, dec, JAN. Нужно привести даты к формату MM-DD-YYYY.

        #формат дат в файле transactions.txt
nov/14/2021
dec/15/2021
12/16/2021
dec/17/2021
jan/03/2022
JAN/10/22

    

Решение:

        import fileinput
import re
fn = "transactions.txt" 
for line in fileinput.input(fn, inplace=True):
    new_line = re.sub('(\d{2}|[a-yA-Y]{3})\/(\d{2})\/(\d{2, 4})', r'\1-\2-\3', line)
    print(new_line)

    
        #Содержимое файла после выполнения кода:
nov-14-2021
dec-15-2021
12-16-2021
dec-17-2021
jan-03-2022
JAN-10-2022

    

Задача 3:

Вводится последовательность строк. Нужно вывести строки, в которых фрагмент «кот» присутствует в качестве подстроки не менее 2 раз.

        #Пример ввода
кот-кот
кот и кот
котофей
котейка кот
кот и котенок

    

Решение:

        import re
import sys
for line in sys.stdin:
    line = line.strip()
    if re.search(r"кот.*?кот", line):
        print(line)

    

Задача 4:

Дана последовательность строк. Нужно вывести те, в которых «кот» встречается в качестве отдельного слова.

        #Пример ввода:
кот в сапогах
кошка и кот
котофей
котяра

    

Решение:

        import re
import sys
for line in sys.stdin:
    line = line.rstrip()
    if re.search(r"\bкот\b", line):
        print(line)

    
        #Вывод
кот в сапогах
кошка и кот

    

Задача 5:

Вывести слова, состоящие из двух одинаковых слогов.

        #Пример ввода
тартар
тик-так
сносно
варвар
барабан

    

Решение:

        import re
import sys
for line in sys.stdin:
    line = line.strip()
    if re.search(r"\b(\w+)\1\b", line):
        print(line)

    
        #Вывод
тартар
сносно
варвар

    

Задача 6:

Вводится последовательность строк. В каждой строке нужно поменять местами две первые буквы в каждом слове, состоящем из двух и более букв.

        #Пример ввода
это пример текста
в котором нужно поменять буквы

    

Решение:

        import sys
import re
for line in sys.stdin:
    line = line.rstrip()
    print(re.sub(r'\b(\w)(\w)', r"\2\1", line))

    
        #Вывод
тэо рпимер еткста
в октором унжно опменять убквы

    

Задача 7:

Напишите функцию для валидации мобильного номера в международном формате. Корректным считается представление номера в таком виде:

        +7(912)15-16-896, 8(912)15-16-896
+79121516896, 89121516896
+7(912)151-68-96, 8(912)151-68-96
+7912-151-6896, 87912-151-6896

    

Решение:

        import re
pattern = re.compile(r'(\+7|8).*?(\d{2,3}).*?(\d{2,3}).*?(\d{2}).*?(\d{2})')
def isValid(number):
    if re.match(pattern, number):
        print("ДА")
    else:
        print("НЕТ")
isValid(input())

    

Задача 8:

Напишите программу для парсинга номеров телефонов с тестовой страницы.

Решение:

        import urllib.request
from re import findall
url = "http://www.summet.com/dmsi/html/codesamples/addresses.html"
response = urllib.request.urlopen(url)
data = response.read()
s = data.decode()
phones = findall("\(\d{3}\) \d{3}-\d{4}", s)
for number in phones:
    print(number)

    

Задача 9:

Нужно извлечь все имена и фамилии из текста.

Решение:

        import re
s = 'На встрече присутствовали: профессор Владимир Успенский, физик-ядерщик Сергей Ковалев, президент клуба Владимир Медведев и космонавт Юрий Титов.'
name = r"[А-Я][а-я]+,?\s+"
last_name = r"[А-Я][а-я]+"
persons = re.findall(name + last_name, s)
for item in persons:
    print(item)

    

Задача 10:

Нужно получить URL всех png и jpg изображений, использованных на главной странице proglib.io:

        import re
import requests
def getURL(text):
    urls = []
    results = re.findall(r'(?:http\:|https\:)?\/\/.*\.(?:png|jpg)', text)
    for x in results:
        if not x.startswith('http:'):
            x = 'http:' + x
            urls.append(x)
    return urls
def getImages(url):
    resp = requests.get(url)
    urls = getURL(resp.text)
    print('urls', urls)
getImages('https://proglib.io')

    

Заключение

Regex в Python – мощный, гибкий, но достаточно сложный инструмент. Регулярные выражения сложно составлять, поддерживать и редактировать. При работе с текстовыми файлами Regex чаще всего можно заменить методами строк, а при парсинге, в большинстве случаев, использование XPath и CSS-селекторов окажется более эффективным.

***

Материалы по теме




МЕРОПРИЯТИЯ

Комментарии

ВАКАНСИИ

Добавить вакансию
Продуктовый аналитик
Екатеринбург, по итогам собеседования
Golang разработчик (middle)
от 230000 RUB до 300000 RUB
Аналитик данных
Екатеринбург, по итогам собеседования

ЛУЧШИЕ СТАТЬИ ПО ТЕМЕ