Функции re.sub() и re.subn() в Python: мощные инструменты замены
Для кого эта статья:
- Python-разработчики, ищущие способы улучшить свои навыки работы с текстом
- Студенты и начинающие программисты, изучающие регулярные выражения
Профессионалы, занимающиеся анализом данных и обработкой текстовой информации
Обработка текстовых данных — одна из ключевых задач, с которой сталкивается каждый Python-разработчик. И когда дело доходит до манипуляций с текстом, регулярные выражения становятся настоящим швейцарским ножом программиста. Особенно полезны функции
re.sub()иre.subn(), которые позволяют не просто находить шаблоны, но и заменять их с хирургической точностью. Эти инструменты радикально упрощают работу с логами, анализ данных и обработку пользовательского ввода, превращая многострочные алгоритмы в элегантные однострочники. 🔍
Хотите профессионально освоить Python и решать сложные задачи обработки текста на реальных проектах? Обучение Python-разработке от Skypro — это погружение в практическое программирование под руководством работающих разработчиков. На курсе вы не только изучите регулярные выражения и функции вроде
re.sub(), но и научитесь применять их в комплексных задачах веб-разработки. Всего за 9 месяцев вы перейдете от основ до создания полноценных приложений с API и базами данных.
Регулярные выражения в Python: базовые принципы работы
Регулярные выражения (regex) — это специализированный язык для описания шаблонов в тексте. В Python для работы с ними используется стандартный модуль re. Прежде чем погрузиться в тонкости работы с функциями re.sub() и re.subn(), необходимо понять фундаментальные принципы построения и использования регулярных выражений.
Чтобы использовать регулярные выражения в 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() — это мощный инструмент для поиска и замены текста на основе регулярных выражений. Она имеет следующий синтаксис:
re.sub(pattern, replacement, string, count=0, flags=0)
Где:
- pattern — регулярное выражение, определяющее, какой текст нужно заменить
- replacement — строка или функция, на которую будет заменено совпадение
- string — исходная строка, в которой производится поиск и замена
- count — максимальное количество замен (по умолчанию 0, что означает "заменить все")
- flags — флаги, модифицирующие поведение регулярного выражения
Функция re.sub() возвращает новую строку с произведёнными заменами. Важно понимать, что строки в Python неизменяемы, поэтому исходная строка остаётся нетронутой.
Рассмотрим несколько примеров использования re.sub():
# Простая замена
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() — использование обратных ссылок в строке замены. Они позволяют обращаться к группам из шаблона и переиспользовать их:
# Форматирование даты
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(). Их синтаксис идентичен:
re.subn(pattern, replacement, string, count=0, flags=0)
Ключевое различие заключается в возвращаемом значении. Если re.sub() возвращает только измененную строку, то re.subn() возвращает кортеж из двух элементов:
- Измененная строка (как в
re.sub()) - Количество произведенных замен
Это дополнительное значение делает re.subn() незаменимым инструментом в ситуациях, когда важно знать не только результат замены, но и количество выполненных операций.
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() для анализа кода:
# Анализ и замена устаревших функций в коде
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), и ожидает получить строку, которая заменит найденное совпадение. Функция вызывается для каждого совпадения отдельно.
Рассмотрим базовую структуру такого подхода:
def replacement_function(match):
# match — объект совпадения (Match object)
# Анализируем совпадение и формируем замену
return "строка замены"
result = re.sub(pattern, replacement_function, input_string)
Преимущества использования функций замены:
- Возможность принимать решения о замене на основе содержимого совпадения
- Выполнение сложных преобразований с данными (например, расчеты, форматирование)
- Доступ к группам захвата через методы объекта Match
- Возможность обращения к внешним данным или состоянию
- Реализация условной логики замены
Примеры использования функций замены:
# Преобразование целых чисел в их шестнадцатеричное представление
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()сэкономило мне дни работы и сделало код намного более гибким и поддерживаемым.
При работе с функциями замены важно учитывать следующие аспекты:
- Функция должна возвращать строку (или объект, приводимый к строке)
- Объект Match предоставляет доступ к найденному тексту и группам через методы
group(),groups(),groupdict() - Для доступа к внешним переменным можно использовать замыкания, глобальные переменные или атрибуты объектов
- Функция может быть анонимной (лямбда-функция) для простых преобразований
Пример использования лямбда-функции для простых преобразований:
# Использование лямбда-функции для простого преобразования
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(), предоставляя не только преобразованный текст, но и количество произведенных замен:
# Подсчет и замена 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. Очистка и подготовка данных
Одно из самых распространенных применений — нормализация и очистка текстовых данных перед анализом:
# Удаление 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(), можно не только преобразовывать текст, но и подсчитывать встречаемость шаблонов:
# Подсчёт и замена 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. Форматирование и преобразование формата данных
Изменение формата дат, чисел и других структурированных данных:
# Преобразование формата даты из 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. Валидация и исправление данных
Проверка корректности данных с исправлением ошибок:
# Проверка и коррекция 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. Обработка и генерация кода
Автоматизация рутинных задач при работе с кодом:
# Замена устаревших 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:
# Токенизация текста
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 в Python: эффективная обработка текста регулярками
- Модуль re в Python: мощный инструмент для обработки текста
- Регулярные выражения в Python: как использовать re.finditer эффективно
- Функции re.sub() и re.subn() в Python: мощные инструменты замены
- Python re.findall: извлечение всех совпадений из текста шаблоном


