Регулярные выражения в Python: мощный инструмент для обработки текста
Для кого эта статья:
- Новички в программировании, особенно те, кто изучает Python
- Python-разработчики, желающие улучшить свои навыки обработки текстовых данных
Специалисты, занимающиеся анализом данных и парсингом информации
Регулярные выражения — это секретное оружие в арсенале каждого Python-разработчика. Они позволяют превратить мучительный парсинг текстовых данных в элегантные решения всего за несколько строк кода. Модуль re в Python часто пугает новичков своим загадочным синтаксисом, напоминающим криптограммы древних цивилизаций. Но поверьте моему опыту: освоив регулярные выражения, вы будете удивляться, как раньше обходились без них. Готовы овладеть этой мощной техникой обработки текста? Давайте начнем разбираться в регулярных выражениях на практических примерах. 🚀
Осваиваете Python и хотите прокачать навыки работы с текстовыми данными? Обучение Python-разработке от Skypro включает углубленное изучение модуля re и регулярных выражений на реальных проектах. Вместо месяцев самостоятельного изучения вы получите структурированные знания от практикующих разработчиков, которые помогут вам автоматизировать обработку текста и анализ данных в течение нескольких недель. Ваше первое регулярное выражение уже ждёт вас!
Модуль re: основы регулярных выражений в Python
Модуль re — встроенный инструмент Python для работы с регулярными выражениями. Регулярные выражения (regex) представляют собой мощный язык шаблонов для поиска, извлечения и манипуляции текстовыми данными. Это как швейцарский нож для работы с текстом — универсальный инструмент, который может сэкономить вам часы программирования. 🔍
Прежде чем погружаться в детали, давайте импортируем модуль re в наш проект:
import re
Когда использовать регулярные выражения? Они незаменимы, когда нужно:
- Проверить формат ввода (email, телефоны, даты)
- Извлечь данные из текста (парсинг)
- Заменить части текста по сложным правилам
- Разбить текст на фрагменты по определённым шаблонам
Алексей Митрофанов, тимлид отдела разработки
Помню, как однажды к нам поступила задача обработать огромную базу клиентских данных. Тысячи записей в разных форматах — телефоны, адреса, имена. Мой младший разработчик пытался написать десятки условий с помощью строковых методов Python. Спустя два дня у него было несколько сотен строк кода, которые всё равно не справлялись со всеми вариантами данных.
Я показал ему модуль re и мы переписали весь функционал. Результат? Вместо сотен строк — всего 20 строк кода с регулярными выражениями. Скорость обработки увеличилась в 10 раз. Это был момент, когда молодой программист понял истинную силу регулярных выражений.
Начнём с понимания основных компонентов регулярных выражений:
| Компонент | Описание | Пример |
|---|---|---|
| Литералы | Обычные символы, которые соответствуют сами себе | python найдёт "python" в тексте |
| Метасимволы | Специальные символы с особым значением | . (точка) соответствует любому символу |
| Квантификаторы | Определяют количество повторений | * — ноль или более раз, + — один или более раз |
| Классы символов | Наборы символов, заключенные в квадратные скобки | [a-z] — любая строчная буква |
Регулярные выражения могут выглядеть устрашающе для новичков, но это лишь вопрос практики. Чтобы облегчить изучение, я рекомендую использовать онлайн-инструменты для тестирования regex, например, regex101.com, который наглядно показывает, как работает ваше выражение.

Базовые функции поиска: re.match(), re.search(), re.findall()
Модуль re предоставляет несколько функций для поиска текста. Три основные функции, которые вы будете использовать чаще всего: match(), search() и findall(). Давайте рассмотрим их в действии и поймем ключевые различия между ними. 🔎
1. re.match() — поиск в начале строки
Функция match() проверяет, соответствует ли начало строки шаблону. Важно понимать: она ищет только в начале строки!
import re
# Поиск слова, начинающегося с "Py"
result = re.match(r"Py", "Python – мощный язык программирования")
print(result) # <re.Match object; span=(0, 2), match='Py'>
# Поиск в середине строки не сработает
result = re.match(r"мощный", "Python – мощный язык программирования")
print(result) # None
2. re.search() — поиск по всей строке
В отличие от match(), функция search() ищет первое совпадение в любой части строки:
# Находит первое совпадение в любом месте строки
result = re.search(r"мощный", "Python – мощный язык программирования")
print(result) # <re.Match object; span=(9, 15), match='мощный'>
3. re.findall() — найти все совпадения
Когда нужно найти все совпадения в тексте, используйте findall(). Эта функция возвращает список всех найденных совпадений:
# Найти все числа в строке
numbers = re.findall(r"\d+", "У меня 3 яблока, 5 груш и 10 апельсинов")
print(numbers) # ['3', '5', '10']
Сравним эти функции в таблице:
| Функция | Область поиска | Возвращаемое значение | Когда использовать |
|---|---|---|---|
| re.match() | Только начало строки | Match объект или None | Валидация строк, проверка заголовков |
| re.search() | Вся строка (первое совпадение) | Match объект или None | Поиск конкретного паттерна в тексте |
| re.findall() | Вся строка (все совпадения) | Список совпадений | Извлечение всех нужных данных |
| re.finditer() | Вся строка (все совпадения) | Итератор Match объектов | Обработка больших текстов с экономией памяти |
Как работать с результатами поиска? Для match() и search() возвращается объект Match, который имеет полезные методы:
group()— возвращает найденное совпадениеstart()— индекс начала совпаденияend()— индекс конца совпаденияspan()— кортеж (start, end)
result = re.search(r"Python", "I love Python programming")
if result:
print(result.group()) # Python
print(result.start()) # 7
print(result.end()) # 13
print(result.span()) # (7, 13)
Синтаксис регулярных выражений на практике
Теперь, когда мы разобрались с основными функциями поиска, давайте углубимся в синтаксис регулярных выражений. Именно эта часть часто вызывает наибольшие трудности у новичков, но я покажу вам, как применять эти знания на практике. 📝
Регулярные выражения имеют свой "алфавит" — специальные символы и конструкции, которые определяют, что именно мы ищем:
.— любой символ, кроме новой строки\d— цифра (эквивалент [0-9])\D— не цифра (эквивалент 0-9)\w— буквенно-цифровой символ (эквивалент [a-zA-Z0-9_])\W— не буквенно-цифровой символ\s— пробельный символ (пробел, табуляция, новая строка)\S— не пробельный символ^— начало строки$— конец строки
Давайте посмотрим, как эти элементы работают на практике:
# Найти все слова, содержащие только буквы
words = re.findall(r"\b[a-zA-Z]+\b", "Python3 is fun123, isn't it?")
print(words) # ['is', 'fun', 'isn', 't', 'it']
# Найти все email адреса
emails = re.findall(r"\b[\w.%+-]+@[\w.-]+\.[a-zA-Z]{2,}\b",
"Свяжитесь с нами: support@example.com или sales@company.org")
print(emails) # ['support@example.com', 'sales@company.org']
Квантификаторы позволяют указать, сколько раз должен повторяться элемент:
*— ноль или более повторений (жадный поиск)+— один или более повторений (жадный поиск)?— ноль или одно повторение{n}— ровно n повторений{n,}— n или более повторений{n,m}— от n до m повторений
Максим Соколов, инженер по анализу данных
Я столкнулся с задачей извлечения информации из неструктурированных текстов контрактов. Каждый документ был отсканирован и распознан с помощью OCR, но форматирование "гуляло", а данные часто располагались в разных местах.
Первый подход с использованием простых строковых поисков приводил к массе ложных срабатываний. Затем я написал регулярное выражение, которое искало паттерны вида "Срок действия: [дата] – [дата]" с учётом различных форматов дат и возможных опечаток.
Самым сложным было учесть все варианты записи дат: "01.01.2023", "1 января 2023", "01/01/23" и т.д. Решением стало такое выражение: \bСрок\s+действия:?\s+(0?[1-9]|[12][0-9]|3[01])[\s./]+(0?[1-9]|1[0-2])[\s./]+(20\d{2}|\d{2})\s[-—–]\s(0?[1-9]|[12][0-9]|3[01])[\s./]+(0?[1-9]|1[0-2])[\s./]+(20\d{2}|\d{2})\b
Это выражение повысило точность извлечения дат до 98%, что позволило автоматизировать обработку более 5000 документов за неделю вместо месяцев ручной работы.
Особое внимание стоит уделить "жадности" квантификаторов. По умолчанию они пытаются захватить максимально возможное количество символов:
# Жадный поиск захватывает всё от первого до последнего тега
result = re.search(r"<.*>", "<p>Это параграф</p><b>с жирным текстом</b>")
print(result.group()) # <p>Это параграф</p><b>с жирным текстом</b>
# Нежадный поиск с '?' захватывает минимум
result = re.search(r"<.*?>", "<p>Это параграф</p><b>с жирным текстом</b>")
print(result.group()) # <p>
При работе с регулярными выражениями в Python рекомендуется использовать raw строки (с префиксом r). Это позволяет избежать конфликтов со спецсимволами Python:
# Без префикса r обратные слеши интерпретируются Python
print(re.findall("\d+", "123")) # Правильно, но опасно
# С префиксом r обратные слеши передаются как есть в регулярное выражение
print(re.findall(r"\d+", "123")) # Предпочтительный способ
Работа с группами захвата и замена текста в Python
Группы захвата — одна из самых мощных возможностей регулярных выражений. Они позволяют не просто найти текст, но и структурировать его, выделяя отдельные части паттерна. Замена текста с использованием групп открывает еще больше возможностей для обработки данных. 🧩
Создание групп захвата
Группы создаются с помощью круглых скобок ():
# Извлечение имени и домена из email
email = "user123@example.com"
match = re.search(r"(.+)@(.+)", email)
if match:
print(f"Username: {match.group(1)}") # Username: user123
print(f"Domain: {match.group(2)}") # Domain: example.com
print(f"Полный email: {match.group(0)}") # Полный email: user123@example.com
Обратите внимание, что:
match.group(0)всегда возвращает полное совпадениеmatch.group(1)возвращает первую группуmatch.group(2)возвращает вторую группу и т.д.
Вы также можете получить все группы сразу:
# Получение всех групп в виде кортежа
match = re.search(r"(\d{2})-(\d{2})-(\d{4})", "Дата рождения: 15-03-1985")
if match:
day, month, year = match.groups()
print(f"День: {day}, Месяц: {month}, Год: {year}") # День: 15, Месяц: 03, Год: 1985
Именованные группы
Для большей ясности кода можно использовать именованные группы с синтаксисом (?P<name>pattern):
# Использование именованных групп для разбора даты
date_pattern = r"(?P<day>\d{2})-(?P<month>\d{2})-(?P<year>\d{4})"
match = re.search(date_pattern, "Событие произошло 21-09-2023 вечером")
if match:
print(f"День: {match.group('day')}") # День: 21
print(f"Месяц: {match.group('month')}") # Месяц: 09
print(f"Год: {match.group('year')}") # Год: 2023
Замена текста с re.sub()
Функция re.sub(pattern, replacement, string) позволяет заменять текст по шаблону:
# Простая замена
censored = re.sub(r"плохое_слово", "***", "Текст с плохое_слово в середине")
print(censored) # Текст с *** в середине
# Замена с использованием групп
# Меняем формат даты с DD-MM-YYYY на YYYY-MM-DD
formatted_date = re.sub(r"(\d{2})-(\d{2})-(\d{4})", r"\3-\2-\1", "21-09-2023")
print(formatted_date) # 2023-09-21
В строке замены \1, \2 и т.д. ссылаются на соответствующие группы захвата.
Для более сложных замен можно использовать функцию:
# Замена с помощью функции – преобразуем числа в слова
def number_to_words(match):
num = int(match.group(0))
words = {1: "один", 2: "два", 3: "три", 4: "четыре", 5: "пять"}
return words.get(num, match.group(0))
text = "У меня есть 2 яблока и 5 апельсинов"
result = re.sub(r"\b[1-5]\b", number_to_words, text)
print(result) # У меня есть два яблока и пять апельсинов
Сравним основные операции с группами и замены:
| Операция | Синтаксис | Применение |
|---|---|---|
| Создание группы | (pattern) | Выделение части шаблона для последующего использования |
| Именованная группа | (?P<name>pattern) | Улучшение читаемости кода при работе со сложными выражениями |
| Не захватывающая группа | (?:pattern) | Группировка без создания захвата (для оптимизации) |
| Ссылка на группу в замене | \n или \g<name> | Использование захваченных групп при замене |
| Функциональная замена | Функция в re.sub() | Сложные преобразования, зависящие от найденного текста |
От простого к сложному: реальные задачи с модулем re
Теперь, когда мы освоили основы, давайте применим наши знания к реальным задачам, с которыми часто сталкиваются Python-разработчики. Это поможет вам увидеть, как регулярные выражения решают практические проблемы. 💻
Задача 1: Валидация email адреса
Проверка корректности email адреса — классическая задача для регулярных выражений:
def is_valid_email(email):
pattern = r"^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$"
return bool(re.match(pattern, email))
# Тестирование
emails = ["user@example.com", "invalid@email", "john.doe@subdomain.example.co.uk", "@missing.com"]
for email in emails:
print(f"{email}: {'✓' if is_valid_email(email) else '✗'}")
Результат:
- user@example.com: ✓
- invalid@email: ✗
- john.doe@subdomain.example.co.uk: ✓
- @missing.com: ✗
Задача 2: Извлечение всех URL из текста
def extract_urls(text):
pattern = r"https?://(?:[-\w.]|(?:%[\da-fA-F]{2}))+(?:/[^\s]*)?"
return re.findall(pattern, text)
content = """Посетите наш сайт http://example.com для получения информации.
Документация доступна на https://docs.example.org/api.
Ссылка на изображение: https://example.com/images/logo.png"""
urls = extract_urls(content)
for i, url in enumerate(urls, 1):
print(f"URL {i}: {url}")
Задача 3: Парсинг и извлечение данных из текста
Допустим, у нас есть текст с информацией о продуктах, и мы хотим извлечь название и цену:
product_list = """
Товар: Ноутбук HP ProBook, Цена: 45000 руб.
Товар: Смартфон Samsung Galaxy, Цена: 25999 руб.
Товар: Клавиатура Logitech, Цена: 3500 руб.
"""
pattern = r"Товар: (.*?), Цена: (\d+) руб\."
matches = re.findall(pattern, product_list)
print("Список товаров:")
for product, price in matches:
print(f"- {product}: {int(price):,} руб.")
Задача 4: Форматирование и очистка данных
Регулярные выражения отлично подходят для нормализации данных:
# Удаление лишних пробелов и нормализация форматирования
def normalize_text(text):
# Заменяем множественные пробелы одним
text = re.sub(r'\s+', ' ', text)
# Удаляем пробелы в начале и конце строки
text = re.sub(r'^\s+|\s+$', '', text)
# Нормализуем форматирование чисел (добавляем пробел после тысяч)
text = re.sub(r'(\d)(\d{3})([^\d]|$)', r'\1 \2\3', text)
return text
messy_text = " Текст с лишними пробелами. Цена: 1000000 руб. "
clean_text = normalize_text(messy_text)
print(clean_text) # "Текст с лишними пробелами. Цена: 1 000000 руб."
Задача 5: Разбор лог-файлов
Анализ лог-файлов — еще одно отличное применение регулярных выражений:
log_line = '192.168.1.1 – - [25/Sep/2023:14:32:52 +0300] "GET /api/users HTTP/1.1" 200 1234'
# Извлекаем IP, дату, метод, путь и код состояния
pattern = r'(\d+\.\d+\.\d+\.\d+).*\[(.*?)\] "(\w+) (.*?) HTTP.*" (\d+)'
match = re.search(pattern, log_line)
if match:
ip, date, method, path, status = match.groups()
print(f"IP: {ip}")
print(f"Дата: {date}")
print(f"Метод: {method}")
print(f"Путь: {path}")
print(f"Статус: {status}")
Советы для сложных регулярных выражений:
- Разбивайте на части: Создавайте сложные выражения поэтапно, тестируя каждую часть отдельно
- Документируйте: Добавляйте комментарии, объясняющие, что делает каждая часть выражения
- Используйте флаги: Флаги re.VERBOSE, re.IGNORECASE могут сделать выражения более читаемыми
- Тестируйте на граничных случаях: Проверяйте выражения на необычных входных данных
- Оптимизируйте: Для частых операций рассмотрите компиляцию выражений с re.compile()
Вот пример использования re.VERBOSE для создания читаемого сложного выражения:
# Валидация сложного пароля с подробными комментариями
password_pattern = re.compile(r"""
^ # Начало строки
(?=.*[A-Z]) # Должна быть хотя бы одна заглавная буква
(?=.*[a-z]) # Должна быть хотя бы одна строчная буква
(?=.*\d) # Должна быть хотя бы одна цифра
(?=.*[@#$%^&+=]) # Должен быть хотя бы один спецсимвол
.{8,} # Минимум 8 символов
$ # Конец строки
""", re.VERBOSE)
passwords = ["Weak", "Strong1@", "NoSpecial1", "nouppercase1@"]
for password in passwords:
is_valid = bool(password_pattern.match(password))
print(f"'{password}': {'Надежный' if is_valid else 'Ненадежный'}")
Регулярные выражения в Python — мощный инструмент, который превращает сложные задачи обработки текста в элегантные решения. Мы рассмотрели основы модуля re, научились использовать функции поиска, работать с группами захвата и применять эти знания к реальным задачам. Помните, что мастерство приходит с практикой: начните с простых выражений и постепенно усложняйте их. Обращайте внимание на производительность при работе с большими объемами данных. И самое главное — не бойтесь экспериментировать! Каждая задача по обработке текста может иметь несколько решений с использованием регулярных выражений. Найдите то, которое подходит именно вам. 🚀