14 — Поиск с регулярными выражениями | Питон и КИЛИ

Поиск с регулярными выражениями

Содержание

Регулярные выражения и их синтаксис

Регулярные выражения (RE, regexp) нужны, чтобы находить в строках подстроки не по точному вхождению, а описываемые правилами-шаблонами.

! Если нужно найти точное вхождение, лучше использовать стандартные методы строк, а не re.

Специальные символы, с помощью которых мы будем задавать правила поиска строк:

  • . один любой символ
  • ? 0 или 1 вхождение предыдущего символа
  • * предыдущий символ повторяется ≥ 0 раз (0, 1, 2, 3 и т. д.)
  • + предыдущий символ повторяется ≥ 1 раз (1, 2, 3 и т. д.)
  • ^ начало строки
  • $ конец строки
  • [abc] «или»: любой из символов а, b, c
  • [а-я] любая буква русского алфавита от «а» до «я» Внутри квадратных скобок большинство специальных символ не действуют: . обозначает точку, ? — вопросительный знак. Вне квадратных скобок, чтобы получить точку или, например, плюс, специальные символы надо экранировать с помощью \ (\. обозначает точку, \+ обозначает плюс).
  • [^abc] — отрицание: любой символ, кроме a, b, c.
  • \d любая цифра, аналогично [0-9]
  • \D — любой символ, кроме цифр (отрицание \d или [^0-9])
  • \w — буквы, цифры, _ (то же, что [a-zA-Z0-9_]), \W — всё кроме букв, цифр, _.
  • \s — любой пробелоподбный символ ([ \t\n\r\f\v]), \S — любой непробелоподбный символ

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

Официальный HOWTO

Мы будем использовать модуль re:

import re

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

first_match = re.search('кот.', 'Кот пришёл к коту и спросил кота: «Бойкот, котелок или скотч?»')

re.search возвращает объект match (или None, если ничего не нашлось), из которого затем можно извлечь результат методом group():

if first_match:
    first_match.group()    # 'кот '
else:
    print('Nothing found.')

Ещё одна полезная функция re.findall(pattern, string) находит все вхождения подходящих строк:

all_results = re.findall('кот.', 'кот пришёл к коту и спросил кота: «Бойкот, котелок или скотч?»')
all_results    # ['кот ', 'коту', 'кота', 'кот,', 'коте', 'котч']

“Сырые” (raw) строки

Для регулярных выражений в питоне лучше использовать синтаксис для «сырых» (raw) строк. Если добавить r в начале строки (r'\d'), то питон будет обрабатывать эту строку не как обычную, а как сырую.

Зачем это нужно? В регулярных выражениях для экранирования специальных символов используется \ – например, \. обозначает точку, \+ – плюс, и т. д. Но когда питон читает обычные, не “сырые” строки (ещё до того, как мы с этой строкой что-то сделали), он тоже использует \ для экранирования символов, но по-другому (например \n – это перенос строки, \t – табуляция, а \\ – буквально символ \). Из-за различий в правилах экранирования может возникнуть путаница – например, при попытке обработать строку \d как обычную, не как “сырую”, питон выдаст синтаксическую ошибку, потому что он не знает, во что преобразовать \d. Другой пример – в синтаксисе регулярных выражений \b обозначает границу слова, а в синтаксисе питоновских строк – символ backspace (поэтому, когда мы не используем “сырые” строки, \b преобразуется в backspace).

Предположим, нам нужно написать регулярное выражение, соответствующее подстроке \section. В синтаксисе регулярных выражений оно будет выглядеть так: \\section (символ \ должен быть экранирован). Но, если мы не используем “сырые” строки, то нам придётся записать его как \\\\section – оба символа \ должны быть экранированы ещё раз, чтобы питон их правильно прочитал. Довольно неудобно.

В “сырых” строках питон ничего не экранирует, а \ читается как обычный символ и экранируется только в регулярном выражении, поэтому путаницы не возникает. Например, на print('Hello!\nHi!') мы получим

Hello!
Hi!

a на print(r'Hello!\nHi!')

Hello!\nHi!

Соответственно, “сырая” строка с регулярным выражением для поиска подстроки \section будет выглядеть просто как r'\\section'.

Кроме того, “сырые” строки удобно использовать для хранения пути к файлам в Windows (например r'C:\Documents\myfile.txt'), так как там тоже используется \.

Ограничение: “сырые” строки не могут содержать нечётное количество символов \ в конце.

Найдем все числа в строке:

digits_list = re.findall(r'\d', 'Я родился 30 февраля 1930 года')
digits_str = ''.join(digits)    # '301930'

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

Задача для всех вариантов – написать 10 функций, использующих регулярные выражения. Решение не должно содержать в себе ничего, кроме функций. Для того, чтобы удалять из текста подстроки, соответствующие определённому паттерну, воспользуйтесь функцией re.sub.

Скелет программы для работы вот такой:

def phone_num_validation(string):
    pass


def date_validation(string):
    pass


def roman_num_validation(string):
    pass


def ipv4_validation(string):
    pass


def whitespace_cleaning(filename):
    pass


def named_entitites_counter(filename):
    pass


def numbering_cleaning(filename):
    pass


def loanwords_extraction(filename):
    pass


def html_tag_extraction(filename):
    pass


def html_tag_cleaning(filename):
    pass

Первые 4 функции принимают на вход строку и возвращают True или False в зависимости от того, является ли строка:

Следующие 4 функции принимают на вход название текстового файла с русским текстом в кодировке UTF-8 и:

  • удаляет из текста все лишние пробелы и переносы строк – заменяет все последовательности из двух и более пробелов или переносов строк на один пробел или перенос строки соотвественно, и возвращает очищенный текст.
  • возвращает частотный словарь (воспользуйтесь классом collections.Counter) всех словоформ имён собственных, кроме тех, которые стоят в начале предложения
  • удаляет из текста нумерацию глав и возвращает очищенный текст. Нумерацией глав считать любые арабские или римские числа, находящиеся на отдельной строке (после числа может стоять одна точка).
  • возвращает список заимствований в тексте. Заимствованиями считать любые последовательности букв латинского алфавита (кроме римских цифр), разделённых пробелами и переносами строк (примеры из тестового текста: 'Hollywood Canteen', 'Black and Tan Fantasy'). Подсказка: пользуйтесь функциями, написанными ранее.

Последние 2 функции принимают на вход название HTML-файла в кодировке UTF-8 и:

  • возвращает список всех HTML-тэгов.
  • чистит текст файла от HTML-тэгов и возвращает очищенный текст.

Тестировать можно, например, на “Пене дней” (txt, html).