Функции re.sub() и re.subn() в Python: мощные инструменты замены

Пройдите тест, узнайте какой профессии подходите
Сколько вам лет
0%
До 18
От 18 до 24
От 25 до 34
От 35 до 44
От 45 до 49
От 50 до 54
Больше 55

Для кого эта статья:

  • Python-разработчики, ищущие способы улучшить свои навыки работы с текстом
  • Студенты и начинающие программисты, изучающие регулярные выражения
  • Профессионалы, занимающиеся анализом данных и обработкой текстовой информации

    Обработка текстовых данных — одна из ключевых задач, с которой сталкивается каждый Python-разработчик. И когда дело доходит до манипуляций с текстом, регулярные выражения становятся настоящим швейцарским ножом программиста. Особенно полезны функции re.sub() и re.subn(), которые позволяют не просто находить шаблоны, но и заменять их с хирургической точностью. Эти инструменты радикально упрощают работу с логами, анализ данных и обработку пользовательского ввода, превращая многострочные алгоритмы в элегантные однострочники. 🔍

Хотите профессионально освоить Python и решать сложные задачи обработки текста на реальных проектах? Обучение Python-разработке от Skypro — это погружение в практическое программирование под руководством работающих разработчиков. На курсе вы не только изучите регулярные выражения и функции вроде re.sub(), но и научитесь применять их в комплексных задачах веб-разработки. Всего за 9 месяцев вы перейдете от основ до создания полноценных приложений с API и базами данных.

Регулярные выражения в Python: базовые принципы работы

Регулярные выражения (regex) — это специализированный язык для описания шаблонов в тексте. В Python для работы с ними используется стандартный модуль re. Прежде чем погрузиться в тонкости работы с функциями re.sub() и re.subn(), необходимо понять фундаментальные принципы построения и использования регулярных выражений.

Чтобы использовать регулярные выражения в Python, первым делом требуется импортировать модуль:

Python
Скопировать код
import re

Основные элементы регулярных выражений можно разделить на несколько категорий:

  • Литералы — обычные символы, которые соответствуют сами себе (например, a соответствует символу "a")
  • Метасимволы — специальные символы с особым значением (. ^ $ * + ? { } [ ] \ | ( ))
  • Квантификаторы — указывают количество повторений (* + ? {n} {n,} {n,m})
  • Группы и ссылки — позволяют группировать части шаблона и обращаться к ним (( ))
  • Классы символов — определяют наборы символов ([ ])
Метасимвол Описание Пример
. Любой символ кроме новой строки a.c соответствует "abc", "a1c" и т.д.
^ Начало строки ^Python соответствует строкам, начинающимся с "Python"
$ Конец строки code$ соответствует строкам, оканчивающимся на "code"
* 0 или более повторений ab*c соответствует "ac", "abc", "abbc" и т.д.
+ 1 или более повторений ab+c соответствует "abc", "abbc", но не "ac"
? 0 или 1 повторение ab?c соответствует "ac" или "abc"
\d Цифра \d{3} соответствует трём цифрам подряд
\w Буквенно-цифровой символ \w+ соответствует одному или более словесным символам

Важно понимать, что Python использует "жадные" квантификаторы по умолчанию — они пытаются захватить максимально возможное количество символов. Добавление ? после квантификатора (*?, +?, ??, {n,m}?) делает его "ленивым" — он захватывает минимально возможное количество символов.

Для поиска совпадений в тексте используются функции re.search(), re.match() и re.findall(). Но когда необходимо не просто найти текст, но и заменить его, на сцену выходят re.sub() и re.subn(). 🧩

Александр Воронов, ведущий Python-разработчик

Когда я только начинал работать с текстовыми данными в Python, у меня был проект по анализу логов серверов. Задача казалась простой — извлечь из тысяч строк все IP-адреса и заменить их на маскированные версии для соблюдения конфиденциальности.

Сначала я пытался использовать стандартные методы строк — split() и replace(). Получился громоздкий код с множеством условий и циклов, который работал медленно и часто ошибался, особенно когда формат логов немного менялся.

Всё изменилось, когда коллега показал мне регулярные выражения и функцию re.sub(). Вместо сотни строк кода решение сократилось до нескольких:

Python
Скопировать код
import re
masked_log = re.sub(r'\b(?:\d{1,3}\.){3}\d{1,3}\b', 'xxx.xxx.xxx.xxx', log_text)

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

Пошаговый план для смены профессии

Функция re.sub() для поиска и замены текста

Функция re.sub() — это мощный инструмент для поиска и замены текста на основе регулярных выражений. Она имеет следующий синтаксис:

Python
Скопировать код
re.sub(pattern, replacement, string, count=0, flags=0)

Где:

  • pattern — регулярное выражение, определяющее, какой текст нужно заменить
  • replacement — строка или функция, на которую будет заменено совпадение
  • string — исходная строка, в которой производится поиск и замена
  • count — максимальное количество замен (по умолчанию 0, что означает "заменить все")
  • flags — флаги, модифицирующие поведение регулярного выражения

Функция re.sub() возвращает новую строку с произведёнными заменами. Важно понимать, что строки в Python неизменяемы, поэтому исходная строка остаётся нетронутой.

Рассмотрим несколько примеров использования re.sub():

Python
Скопировать код
# Простая замена
text = "Мой телефон 123-456-7890 и 987-654-3210"
result = re.sub(r'\d{3}-\d{3}-\d{4}', 'XXX-XXX-XXXX', text)
print(result) # Выведет: "Мой телефон XXX-XXX-XXXX и XXX-XXX-XXXX"

# Ограничение количества замен
result = re.sub(r'\d{3}-\d{3}-\d{4}', 'XXX-XXX-XXXX', text, count=1)
print(result) # Выведет: "Мой телефон XXX-XXX-XXXX и 987-654-3210"

# Использование групп в шаблоне и обратных ссылок в замене
text = "Иванов Иван, Петров Петр"
result = re.sub(r'(\w+) (\w+)', r'\2 \1', text)
print(result) # Выведет: "Иван Иванов, Петр Петров"

Один из мощнейших аспектов re.sub() — использование обратных ссылок в строке замены. Они позволяют обращаться к группам из шаблона и переиспользовать их:

Python
Скопировать код
# Форматирование даты
date_text = "2023-11-15"
formatted = re.sub(r'(\d{4})-(\d{2})-(\d{2})', r'\3.\2.\1', date_text)
print(formatted) # Выведет: "15.11.2023"

# Обработка HTML-тегов
html = "<div>Содержимое</div>"
content_only = re.sub(r'<[^>]+>(.+?)</[^>]+>', r'\1', html)
print(content_only) # Выведет: "Содержимое"

При работе с re.sub() полезно учитывать следующие практические рекомендации:

  • Используйте сырые строки (r'...') для регулярных выражений, чтобы избежать проблем с экранированием
  • Для сложных шаблонов предварительно компилируйте регулярное выражение с помощью re.compile(), особенно если оно используется многократно
  • Применяйте флаги для расширенных возможностей, например re.IGNORECASE для регистронезависимого поиска или re.DOTALL, чтобы символ точки соответствовал и новой строке
  • Будьте осторожны с метасимволами в строке замены — они могут требовать экранирования

При работе с большими объёмами данных производительность re.sub() становится критически важным фактором. Неоптимальные регулярные выражения могут значительно замедлить работу программы. 🚀

Особенности и преимущества метода re.subn()

Функция re.subn() является менее известным, но чрезвычайно полезным родственником re.sub(). Их синтаксис идентичен:

Python
Скопировать код
re.subn(pattern, replacement, string, count=0, flags=0)

Ключевое различие заключается в возвращаемом значении. Если re.sub() возвращает только измененную строку, то re.subn() возвращает кортеж из двух элементов:

  1. Измененная строка (как в re.sub())
  2. Количество произведенных замен

Это дополнительное значение делает re.subn() незаменимым инструментом в ситуациях, когда важно знать не только результат замены, но и количество выполненных операций.

Python
Скопировать код
import re

text = "Python программирование. Python разработка. Python обучение."
new_text, count = re.subn(r'Python', 'Java', text)

print(new_text) # "Java программирование. Java разработка. Java обучение."
print(count) # 3

Функция re.subn() особенно полезна в следующих сценариях:

  • Подсчёт встречаемости шаблонов при их одновременной замене
  • Отладка регулярных выражений — можно быстро проверить, сколько замен произвелось
  • Условная логика на основе количества замен — например, выполнение дополнительных действий, только если было произведено определённое количество замен
  • Валидация данных — проверка, что текст содержит ожидаемое количество вхождений определенного шаблона

Рассмотрим пример, демонстрирующий преимущества использования re.subn() для анализа кода:

Python
Скопировать код
# Анализ и замена устаревших функций в коде
code = """
def old_function1():
pass

def new_function():
pass

def old_function2():
pass
"""

new_code, replacements = re.subn(r'def old_function\d*\(\):', r'def updated_function():', code)

if replacements > 0:
print(f"Обновлено {replacements} устаревших функций.")
print("Новый код:")
print(new_code)
else:
print("Устаревших функций не обнаружено.")

Характеристика re.sub() re.subn()
Возвращаемое значение Только измененная строка Кортеж (строка, количество замен)
Применение Когда важен только результат замены Когда нужно знать количество произведенных замен
Производительность Немного быстрее (меньше операций) Незначительно медленнее из-за подсчета
Использование с функцией замены Поддерживает Поддерживает
Обработка ошибок Сложнее определить причину отсутствия замен Легче отладить — видно, сколько замен произошло
Типичные сценарии Обычная замена текста Замена с аудитом или условной логикой

При выборе между re.sub() и re.subn() следует руководствоваться конкретными требованиями задачи:

  • Если требуется только заменить текст — используйте re.sub()
  • Если нужно знать количество произведённых замен или применить логику на основе этого количества — выбирайте re.subn()

Важное практическое применение re.subn() — мониторинг и анализ изменений в тексте. Это особенно полезно при работе с большими объемами данных, где ручной подсчет нецелесообразен. 📊

Сложные шаблоны замен с использованием функций

Одной из наиболее мощных возможностей функций re.sub() и re.subn() является использование функций обратного вызова (callback) вместо строк замены. Это позволяет реализовать динамические и контекстно-зависимые замены, которые невозможно выполнить с помощью простых шаблонов.

При использовании функции в качестве второго аргумента, Python передаёт ей объект совпадения (Match object), и ожидает получить строку, которая заменит найденное совпадение. Функция вызывается для каждого совпадения отдельно.

Рассмотрим базовую структуру такого подхода:

Python
Скопировать код
def replacement_function(match):
# match — объект совпадения (Match object)
# Анализируем совпадение и формируем замену
return "строка замены"

result = re.sub(pattern, replacement_function, input_string)

Преимущества использования функций замены:

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

Примеры использования функций замены:

Python
Скопировать код
# Преобразование целых чисел в их шестнадцатеричное представление
def to_hex(match):
number = int(match.group(0))
return hex(number)

text = "Десятичные числа: 10, 15, 255"
result = re.sub(r'\b\d+\b', to_hex, text)
print(result) # Десятичные числа: 0xa, 0xf, 0xff

# Капитализация слов в зависимости от их длины
def conditional_capitalize(match):
word = match.group(0)
if len(word) > 4:
return word.upper()
return word.lower()

text = "Python JavaScript Java Ruby PHP"
result = re.sub(r'\b\w+\b', conditional_capitalize, text)
print(result) # python JAVASCRIPT JAVA ruby php

# Изменение формата даты с проверкой валидности
def format_date(match):
year, month, day = match.groups()
month_int = int(month)
if month_int < 1 or month_int > 12:
return f"{year}-{month}-{day} (Invalid month)"
return f"{day}.{month}.{year}"

text = "Даты: 2023-01-15, 2023-13-01, 2022-05-30"
result = re.sub(r'(\d{4})-(\d{2})-(\d{2})', format_date, text)
print(result) # Даты: 15.01.2023, 2023-13-01 (Invalid month), 30.05.2022

Михаил Корнев, специалист по анализу данных

В одном из проектов по анализу текстовых отзывов клиентов нашего сервиса мне нужно было извлечь все упоминания цен и нормализовать их к единому формату для последующего анализа. Проблема заключалась в том, что пользователи записывали суммы по-разному: "100р", "100 рублей", "100руб.", "100 р." и т.д.

Мой первый подход — написать десятки условий для каждого формата — быстро превратился в неподдерживаемый код. Тогда я решил использовать re.sub() с функцией замены:

Python
Скопировать код
def normalize_price(match):
price_text = match.group(0)
# Извлекаем только цифры
digits = re.search(r'\d+', price_text).group(0)
return digits + " руб."

# Шаблон захватывает различные форматы записи цен
price_pattern = r'\b\d+\s*(?:р|руб|рублей|р\.|руб\.)\b'
normalized_text = re.sub(price_pattern, normalize_price, reviews_text)

Это решение не только справилось с задачей нормализации, но и позволило легко добавлять новые форматы цен, просто расширяя регулярное выражение. А когда потребовалось сконвертировать цены в копейки для анализа, я просто изменил функцию замены, не трогая основной код.

Использование функции замены в re.sub() сэкономило мне дни работы и сделало код намного более гибким и поддерживаемым.

При работе с функциями замены важно учитывать следующие аспекты:

  1. Функция должна возвращать строку (или объект, приводимый к строке)
  2. Объект Match предоставляет доступ к найденному тексту и группам через методы group(), groups(), groupdict()
  3. Для доступа к внешним переменным можно использовать замыкания, глобальные переменные или атрибуты объектов
  4. Функция может быть анонимной (лямбда-функция) для простых преобразований

Пример использования лямбда-функции для простых преобразований:

Python
Скопировать код
# Использование лямбда-функции для простого преобразования
text = "Температура: 20C, 25C, 30C"
result = re.sub(r'(\d+)C', lambda m: f"{int(m.group(1)) * 9/5 + 32:.1f}F", text)
print(result) # Температура: 68.0F, 77.0F, 86.0F

Функции замены также отлично работают с re.subn(), предоставляя не только преобразованный текст, но и количество произведенных замен:

Python
Скопировать код
# Подсчет и замена email-адресов на маскированные версии
def mask_email(match):
email = match.group(0)
username, domain = email.split('@')
masked = username[0] + '*' * (len(username) – 1) + '@' + domain
return masked

text = "Контакты: user1@example.com, admin@server.org, info@domain.net"
result, count = re.subn(r'\b[\w.%+-]+@[\w.-]+\.[a-zA-Z]{2,}\b', mask_email, text)
print(result) # Контакты: u***@example.com, a****@server.org, i***@domain.net
print(f"Замаскировано {count} email-адресов") # Замаскировано 3 email-адресов

Функции замены — это то, что выводит возможности re.sub() и re.subn() далеко за рамки простой замены текста, превращая их в мощный инструмент для сложной обработки и анализа данных. 🛠️

Практические кейсы применения re.sub() и re.subn()

Функции re.sub() и re.subn() находят применение в широком спектре практических задач обработки текста. Рассмотрим наиболее типичные и полезные сценарии их использования, которые можно адаптировать под собственные проекты.

1. Очистка и подготовка данных

Одно из самых распространенных применений — нормализация и очистка текстовых данных перед анализом:

Python
Скопировать код
# Удаление HTML-тегов из текста
clean_text = re.sub(r'<[^>]+>', '', html_text)

# Нормализация пробелов (замена нескольких пробелов одним)
normalized_text = re.sub(r'\s+', ' ', text).strip()

# Удаление всех символов, кроме букв и пробелов
letters_only = re.sub(r'[^a-zA-Zа-яА-ЯёЁ\s]', '', text)

# Удаление стоп-слов
stop_words = ['the', 'and', 'is', 'in', 'at', 'of']
pattern = r'\b(?:' + '|'.join(stop_words) + r')\b'
filtered_text = re.sub(pattern, '', text)

2. Анализ и извлечение информации

Используя re.subn(), можно не только преобразовывать текст, но и подсчитывать встречаемость шаблонов:

Python
Скопировать код
# Подсчёт и замена email-адресов
masked_text, email_count = re.subn(
r'\b[\w.%+-]+@[\w.-]+\.[a-zA-Z]{2,}\b', 
'[EMAIL PROTECTED]', 
text
)
print(f"Найдено {email_count} email-адресов")

# Подсчёт упоминаний продукта и замена на актуальное название
new_text, mentions = re.subn(r'\bСтарое название\b', 'Новое название', text)
if mentions > 10:
print("Продукт часто упоминается в тексте")

3. Форматирование и преобразование формата данных

Изменение формата дат, чисел и других структурированных данных:

Python
Скопировать код
# Преобразование формата даты из YYYY-MM-DD в DD.MM.YYYY
formatted_dates = re.sub(
r'(\d{4})-(\d{2})-(\d{2})', 
r'\3.\2.\1', 
text
)

# Форматирование телефонных номеров
standardized = re.sub(
r'(\+?7|8)[\s\-]?\(?(\d{3})\)?[\s\-]?(\d{3})[\s\-]?(\d{2})[\s\-]?(\d{2})', 
r'+7 (\2) \3-\4-\5', 
text
)

# Преобразование чисел из одной системы счисления в другую
def to_base(match):
decimal = int(match.group(1))
return f"0x{decimal:X}" # Преобразование в шестнадцатеричный формат

hex_numbers = re.sub(r'decimal\((\d+)\)', to_base, text)

4. Валидация и исправление данных

Проверка корректности данных с исправлением ошибок:

Python
Скопировать код
# Проверка и коррекция URL-адресов
def validate_url(match):
url = match.group(0)
if not url.startswith('http'):
return 'https://' + url
return url

corrected_text = re.sub(r'\b(?:https?:\/\/)?[\w\.-]+\.[a-z]{2,}\b', validate_url, text)

# Исправление распространённых опечаток
typos = {
'langauge': 'language',
'programing': 'programming',
'pythno': 'python'
}
pattern = r'\b(?:' + '|'.join(map(re.escape, typos.keys())) + r')\b'

def correct_typo(match):
return typos[match.group(0).lower()]

corrected_text = re.sub(pattern, correct_typo, text, flags=re.IGNORECASE)

5. Обработка и генерация кода

Автоматизация рутинных задач при работе с кодом:

Python
Скопировать код
# Замена устаревших API-вызовов на новые
updated_code = re.sub(
r'oldAPI\.method\(([^)]+)\)', 
r'newAPI.improvedMethod(\1, config=DEFAULT_CONFIG)', 
code
)

# Добавление логирования к функциям
def add_logging(match):
func_name = match.group(1)
return f'def {func_name}(*args, **kwargs):\n logger.debug("Вызов {func_name}")'

instrumented_code = re.sub(r'def\s+(\w+)\s*\(', add_logging, code)

# Оптимизация импортов в Python
consolidated, count = re.subn(
r'import\s+([\w.]+)\nimport\s+([\w.]+)', 
r'import \1, \2', 
code
)
print(f"Объединено {count} импортов")

Область применения Функция Типичные задачи
Анализ данных re.sub() и re.subn() Очистка, нормализация, извлечение информации
Веб-разработка re.sub() Обработка URL, валидация форм, обработка HTML
Обработка логов re.subn() Подсчёт событий, маскирование конфиденциальных данных
Парсинг документов re.sub() с функциями Извлечение структурированной информации, преобразование форматов
Работа с кодом Оба метода Рефакторинг, автогенерация кода, документирование
Обработка текста re.sub() Исправление опечаток, форматирование, цензура

6. Обработка естественного языка

Предварительная обработка текста перед применением методов NLP:

Python
Скопировать код
# Токенизация текста
tokens = re.sub(r'[^\w\s]', ' ', text.lower())
tokens = re.sub(r'\s+', ' ', tokens).strip().split()

# Удаление стоп-слов и редких терминов
stop_words = ['the', 'and', 'is', 'in', 'at', 'of']
pattern = r'\b(?:' + '|'.join(stop_words) + r')\b'
processed_text = re.sub(pattern, '', text, flags=re.IGNORECASE)

# Лемматизация текста с помощью словаря
lemma_dict = {'running': 'run', 'runs': 'run', 'ran': 'run'}
pattern = r'\b(?:' + '|'.join(map(re.escape, lemma_dict.keys())) + r')\b'

def lemmatize(match):
return lemma_dict[match.group(0).lower()]

lemmatized = re.sub(pattern, lemmatize, text, flags=re.IGNORECASE)

При работе с этими функциями в реальных проектах, рекомендуется:

  • Тестировать регулярные выражения на различных наборах данных
  • Компилировать сложные или часто используемые шаблоны для повышения производительности
  • Документировать неочевидные регулярные выражения с помощью комментариев
  • Разбивать сложные задачи на несколько последовательных вызовов re.sub()
  • Использовать именованные группы для улучшения читабельности кода

Функции re.sub() и re.subn() — это не просто инструменты замены текста, а многофункциональные помощники, которые могут значительно упростить и автоматизировать работу с текстовыми данными любой сложности. 🚀

Освоение функций re.sub() и re.subn() открывает перед Python-разработчиком новый уровень возможностей работы с текстом. Эти инструменты позволяют превратить сложные многоэтапные процессы обработки данных в элегантные, эффективные решения. Регулярные выражения, несмотря на свою кажущуюся сложность, становятся незаменимыми союзниками в вашем арсенале, когда вы понимаете принципы их работы и умело применяете в комбинации с функциями замены. И помните — мастерство в работе с регулярными выражениями приходит с практикой. Экспериментируйте, тестируйте и создавайте.

Читайте также

Проверь как ты усвоил материалы статьи
Пройди тест и узнай насколько ты лучше других читателей
Что делает функция re.sub()?
1 / 5

Загрузка...