На прошлом занятии

  • Сырые строки r''
  • Метод group()
  • Запоминающие группы
  • Ограничение жадности квантификаторов

План этого занятия

  • Замены - функция re.sub(pattern, repl, string)
  • Именованные группы

Замены в регурярных выражениях

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

>>> string = 'Алёна мыла ёлку!'
>>> print(string.replace('ё', 'е'))
Алена мыла елку!

Но, что, делать в более сложных случаях? Ну не писать же несколько раз вызов функции .replace() с разными аргументами

>>> string = 'Ёлку мыла Алёна...'
>>> print(string.replace('ё', 'е').replace('Ё', 'Е'))
Елку мыла Алена...

На помощь приходят регулярные выражения и модуль re со своей функцией re.sub().

Сигнатура методы такая: re.sub(pattern, repl, string), где

  • pattern - это регулярное выражение - шаблон для поиска строки, которую нужно заменить
  • repl - строка, на которую нужно произвести замену
  • string - сама строка, над которой нужно произвести манипуляции

Метод re.sub() ищет шаблон pattern в строке string и заменяет его на repl. Метод возвращает новую строку. Если шаблон не найден в строке, то текст возвращается без изменений.

>>> # Задача: заменить все числа на слово NUM
>>> #
>>> import re
>>> string = 'Мой дядя родился в 48 году и в 2000 ему было 52'
>>> pattern = '[0-9]+'
>>> print(re.sub(pattern, 'NUM', string))
Мой дядя родился в NUM году и в NUM ему было NUM

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

Использование групп при заменах

Представьте, что вам нужно написать функцию, которая будет менять американский формат записи даты MM/DD/YYYY на русский DD.MM.YYYY. Сейчас не будем говорить, про то, что дни могут быть только в диапазоне от 1 до 31, а месяцы от 1 до 12.

Функция может иметь слудующий вид:

def convert_dates(text):

    pattern = '([0-9]{2})/([0-9]{2})/([0-9]{4})'
    result = re.search(pattern, text)

    if result:
        mm = result.group(1)
        dd = result.group(2)
        yyyy = result.group(3)
        new_date = dd + '/' + mm + '/' + yyyy
        start, stop = result.span()
        text = text[:start] + new_date + text[stop:]

    return text

И работать так:

>>> convert_dates('Я влюбился в тебя 03/21/2017.')
'Я влюбился в тебя 21/03/2017.'

Но, что если, дат в тексте будет больше, чем одна. Да и вообще, неужели нужно писать столь сложный код для такой логики?

На помощь приходят группы. Функцию выше можно переписать так:

def convert_dates(text):

    pattern = '([0-9]{2})/([0-9]{2})/([0-9]{4})'
    repl = r'\2/\1/\3'

    return re.sub(pattern, repl, text)

А использовать так же:

>>> convert_dates('Я влюбился в тебя 03/21/2017. Мои родители познакомились 03/21/1999')
'Я влюбился в тебя 21/03/2017. Мои родители познакомились 21/03/1999'

Здесь repl - это еще один шаблон, который говорит функции re.sub() куда вставить ту или иную группы из шаблона pattern. Выглядит конечно страшно, но куда деваться.

Как, наверное, можно догадаться, \N - это указание на конкретную группу из шаблона pattern, которую нужно подставить. N - это номер группы.

Именованные группы

Когда количество групп в шаблонах увеличивается, то становится трудно с ними работать. Можно легко запутаться в индексах и допустить ошибку. Здесь на помощь приходят именованные группы, с помощью которых можно дать каждой группе своё имя. Осталось только привыкнуть к синтаксису.

Что, если, в наш пример с датой добавится еще и время…

def convert_dates(text):

    pattern = '(?P<m>[0-9]{2})/(?P<d>[0-9]{2})/(?P<y>[0-9]{4}) (?P<hm>[0-9]{2}:[0-9]{2})'
    repl = r'\g<d>/\g<m>/\g<y> в \g<hm>'

    return re.sub(pattern, repl, text)
>>> convert_dates('Я влюбился в тебя 03/21/2017 23:45 по московскому времени')
'Я влюбился в тебя 21/03/2017 в 23:45 по московскому времени'

(?P<m>[0-9]{2}) - ?P<m> - специальный синтаксис задания имени группы. Имя здесь только то, что заключено в скобки.

Обращение к группе происходит тоже с помощью спецального синтаксиса: \g<d>

Имена групп можно использовать в методе .group()

def get_mail_provider(email):

    pattern = '(?P<login>[a-zA-Z0-9_]+)@(?P<provider>(?P<name>[a-zA-Z0-9_]+)\.(?P<domain>[a-zA-Z]+))'
    result = re.search(pattern, email)

    if result:
        return result.group('provider')

    return None
>>> get_mail_provider('ivan@yandex.ru')
'yandex.ru'

Домашнее задание

Вам нужно сохранить у себя на компьютере статью из Википедии, указанную в задании. Файл, естественно, должен быть в UTF-8. Программа должна читать этот файл и заменять в нём все формы слова A на соответствующие формы слова B (слова A и B тоже указаны в задании для Вашего варианта). То, что получится, она должна записывать в другой текстовый файл. Заменяться должны только формы этого слова. Т. е. если Вам нужно заменить слово “кит” на слово “кот”, слово “китовый” на слово “котовый” заменяться не должно. При замене нужно пользоваться функцией re.sub. Если слово было написано с большой буквы, то и после замены оно должно быть написано с большой буквы.

  1. статья “комар”, заменить “комар” на “слон”;
  2. статья “викинги”, заменить “викинг” на “бурундук”;
  3. статья “лингвистика”, заменить “язык” на “шашлык”;
  4. статья “философия”, заменить “философия” на “астрология”;
  5. статья “динозавр”, заменить “динозавр” на “кот”;
  6. статья “Финляндия”, заменить “Финляндия” на “Малайзия”;
  7. статья “птицы”, заменить “птица” на “рыба”.

Выполните задание пройдя по ссылке в GitHub Classroom:


Улучшить эту страницу