Урок 5 из 8 · 25 мин · обновлено

Текст и даты: поиск, замена, регулярные выражения

Как приводить текст в порядок на Python: методы строк, регулярные выражения для телефонов, почты и номеров документов, работа с датами через datetime — разбор, формат, сроки и правильная сортировка.

Методы строк

Многие задачи с текстом решаются без сложных инструментов — встроенными методами строк:

Python
name = "  Ручка ГЕЛЕВАЯ  "

print(name.strip())               # «Ручка ГЕЛЕВАЯ» — без пробелов по краям
print(name.strip().lower())       # «ручка гелевая»
print(name.strip().capitalize())  # «Ручка гелевая»
print("45,50".replace(",", "."))  # «45.50» — теперь можно float()
print("Тетрадь;48 л.;12".split(";"))  # ['Тетрадь', '48 л.', '12']

Привести названия товаров к одному виду перед подсчётом — частая задача: иначе «Маркер» и «маркер » окажутся в отчёте двумя разными товарами.

Регулярные выражения

Когда нужно найти в тексте что-то по шаблону — все телефоны, все адреса почты, номер накладной, — используют регулярные выражения. Это маленький язык шаблонов, в Python он живёт в модуле re.

Основные обозначения:

Шаблон Что означает
\d любая цифра
\D любой символ, кроме цифры
\w буква, цифра или подчёркивание
. любой символ; сама точка — \.
+ предыдущее — один раз или больше
{4} предыдущее — ровно четыре раза
[...] один из символов в скобках
(...) группа: эту часть совпадения можно достать отдельно

Три главные функции:

  • re.findall(шаблон, текст) — найти все совпадения;
  • re.search(шаблон, текст) — найти первое совпадение;
  • re.sub(шаблон, замена, текст) — заменить все совпадения.

Шаблоны пишут в строках с буквой r перед кавычками: r"\d+". Так Python не трогает обратные косые черты.

Приводим в порядок телефоны

Перед рассылкой нужно привести телефоны клиентов к одному виду: +79123456789. В таблице они записаны как попало:

Текст
имя;телефон
Ольга;8 (912) 345-67-89
Игорь;+7 912 111 22 33
Анна;9125550011
Пётр;8-912-000
Python
import csv
import re

def normalize_phone(raw):
    digits = re.sub(r"\D", "", raw)          # оставляем только цифры
    if len(digits) == 11 and digits[0] in "78":
        digits = digits[1:]
    if len(digits) != 10:
        return None                           # номер неполный — пусть проверит человек
    return "+7" + digits

with open("клиенты.csv", encoding="utf-8-sig", newline="") as f:
    for row in csv.DictReader(f, delimiter=";"):
        phone = normalize_phone(row["телефон"])
        print(row["имя"], phone or "проверить вручную: " + row["телефон"])
Результат
Ольга +79123456789
Игорь +79121112233
Анна +79125550011
Пётр проверить вручную: 8-912-000

Сначала re.sub(r"\D", "", raw) выбрасывает всё, кроме цифр. Потом убираем первую 8 или 7 и проверяем, что осталось ровно 10 цифр. Номер Петра неполный — скрипт его не угадывает, а честно помечает. Это важное правило автоматизации: данные, которые скрипт не понял, должен проверить человек.

Находим почту и номера в тексте

Из письма поставщика нужно достать адреса почты и номера накладных:

Python
import re

text = """Добрый день! Счёт пришлите на buh@kancmarket.ru,
копию — Светлане (svetlana@example.ru). Спасибо!"""

emails = re.findall(r"[\w.-]+@[\w-]+\.[a-z]{2,}", text)
print(emails)
Результат
['buh@kancmarket.ru', 'svetlana@example.ru']

Шаблон читается так: несколько букв, цифр, точек или дефисов, затем @, затем имя домена, точка и не меньше двух латинских букв. Он упрощённый — для рабочих задач его обычно хватает.

С группами можно достать только нужную часть совпадения. Например, номера накладных без знака «№»:

Python
text = "Направляем накладные №1406 от 14.10.2026 и №1407 от 16.10.2026."
print(re.findall(r"№(\d+)", text))   # ['1406', '1407']

Даты

Даты в Python — модуль datetime. Две главные операции:

  • datetime.strptime(текст, шаблон) — разобрать текст в дату;
  • дата.strftime(шаблон) — записать дату текстом.

В шаблоне %d — день, %m — месяц, %Y — год из четырёх цифр. Добавить или отнять дни помогает timedelta:

Python
from datetime import date, datetime, timedelta

delivery = datetime.strptime("28.10.2026", "%d.%m.%Y").date()
payment_due = delivery + timedelta(days=14)

print("Оплатить до:", payment_due.strftime("%d.%m.%Y"))
print("Осталось дней:", (payment_due - date(2026, 10, 30)).days)
Результат
Оплатить до: 11.11.2026
Осталось дней: 12

Разница двух дат — тоже timedelta, а .days даёт число дней. В рабочем скрипте вместо date(2026, 10, 30) будет date.today().

Даты в именах файлов

Отчёты лежат в папке с именами «отчёт 5.10.2026.xlsx», «отчёт 12.10.2026.xlsx», «отчёт 28.9.2026.xlsx». Отсортируйте их — и порядок окажется странным:

Python
import re
from datetime import datetime

names = ["отчёт 5.10.2026.xlsx", "отчёт 12.10.2026.xlsx", "отчёт 28.9.2026.xlsx"]

for name in sorted(names):
    match = re.search(r"(\d{1,2})\.(\d{1,2})\.(\d{4})", name)
    day = datetime(int(match[3]), int(match[2]), int(match[1]))
    print(f"{name} → {day:%Y-%m-%d} отчёт.xlsx")
Результат
отчёт 12.10.2026.xlsx → 2026-10-12 отчёт.xlsx
отчёт 28.9.2026.xlsx → 2026-09-28 отчёт.xlsx
отчёт 5.10.2026.xlsx → 2026-10-05 отчёт.xlsx

Слева видно, что текст сортируется посимвольно: «1» раньше «2», а «2» раньше «5». Справа те же даты в виде год-месяц-день — в таком виде они сортируются правильно, даже как обычный текст. match[1], match[2], match[3] — группы шаблона: день, месяц, год. Переименовать файлы можно приёмом из урока 2 с пробным запуском.

Попробуйте сами. В письме поставщика: «Направляем накладные №1406 от 14.10.2026 и №1407 от 16.10.2026. Оплата — в течение 14 дней». Найдите все даты и для каждой посчитайте срок оплаты.

Решение
Python
import re
from datetime import datetime, timedelta

text = "Направляем накладные №1406 от 14.10.2026 и №1407 от 16.10.2026. Оплата — в течение 14 дней."

for found in re.findall(r"\d{2}\.\d{2}\.\d{4}", text):
    due = datetime.strptime(found, "%d.%m.%Y") + timedelta(days=14)
    print(found, "→", f"{due:%d.%m.%Y}")
Результат
14.10.2026 → 28.10.2026
16.10.2026 → 30.10.2026

\d{2}\.\d{2}\.\d{4} — две цифры, точка, две цифры, точка, четыре цифры. Точку в шаблоне экранируют \., иначе она означает «любой символ».

Мини-тест · 4 вопроса

Проверьте себя

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

  1. Что вернёт re.sub(r"\D", "", "8 (912) 345-67-89")?

  2. Как превратить текст «28.10.2026» в дату?

  3. Почему файлы «отчёт 12.10.2026», «отчёт 28.9.2026», «отчёт 5.10.2026» сортируются в странном порядке?

  4. Скрипт нормализации получил номер «8-912-000» и вернул None. Почему так лучше, чем угадывать номер?