Время прочтения: 6 мин.
Я искал информацию в интернете и часто встречал статьи, содержащие описание документации, либо статьи с отсутствием примеров, что ухудшало понимание, либо с примерами шаблонов, но из той же самой документации. Здесь же я собрал несколько примеров, встречавшихся в моей работе, и способы их решения. Надеюсь, вы узнаете что-то новое и полезное.
Улучшаю читаемость
Рано или поздно большинству разработчиков приходится читать свой старый код и пытаться вспомнить за что отвечал тот или иной участок кода. Все это происходит из-за отсутствия документации в коде.
Например, такой паттерн для поиска номеров сотовых операторов РФ воспринимается трудно:
pattern = r'((8|\+7)[\- ]?)?(\(?\d{3}\)?[- ]?)?[\d\- ]{7,10}'
Для решения этой проблемы следует использовать комментарии внутри паттерна регулярного выражения (?#…).
К примеру, возьму простой паттерн поиска номера телефона с жесткой структурой:
pattern = r'\+7\(\d{3}\)(?# код оператора)\d{3}(?# первые 3 цифры номера)-\d{2}-\d{2}(?# последние 4 разделенные по 2)'
Комментарии есть, но они записаны в 1 строку и усложняют восприятие паттерна. В своей практике я использую такой вид:
pattern = '''+7\(\d{3}\) # код оператора
\d{3}- # первые 3 цифры номера
\d{2}-\d{2} # последние 4 разделенные по 2
'''
Читаемость кода намного улучшается. Но если использовать такой паттерн, то результатом работы кода будет пустое значение. Для того, чтобы код отработал так, как мы хотим, нужно добавить модификатор или, по-другому, флаг re.X или re.VERBOSE, который позволяет использовать многострочный синтаксис.
text = 'Мои номера телефона +7(923)111-22-33 и +7(923)444-55-66 звонить с 9:00 до 18:00'
re.findall(pattern, text, re.X)
Вывод:
Таким образом, комментарии добавляют ясность в код и, при прочтении через длительный срок, не нужно будет вспоминать, за что отвечает каждый участок выражения.
Ссылки на группы
Иногда требуется найти повторяющиеся слова, символы или числа в предложении. С помощью методов преобразования строк это делать очень долго и приходится учитывать множество условий. Чем больше в коде переменных и условий, тем больше вероятность сделать ошибку. Регулярными выражениями это сделать намного проще и быстрее, не обращая внимания на знаки препинания.
Возьму датафрейм с дублями чисел, слов:
В поиске дублей поможет группировка:
pattern = r'\b([a-zA-Zа-яА-Я0-9]+)\b.+\1'
Конструкция \1 используется как ссылка, которая указывает на то, что после слова или числа, которое подошло под условия, прописанные в скобках, через произвольное количество символов должно идти тоже самое слово или число. Тогда при использовании этого паттерна:
df['Организация'].apply(lambda x: '' if re.search(pattern, x) == None else re.search(pattern, x).group(0))
Вижу дубликаты:
Удалю их.
def clean_text(x):
wh = re.search(pattern, x).group(0)
fwh = re.search(pattern, x).group(1)
return re.sub(wh, fwh, x)
df['Организация'].apply(lambda x: clean_text(x))
И на выходе получится чистый текст без дубликатов слов в каждой строке:
Рассмотрю ещё один пример применения ссылок на группы. Нужно найти слово, которое вначале было в верхнем регистре, а затем в нижнем. Возьму для примера текст:
text = r'''PYTHON (питОн или пАйтон) - высокоуровневый язык программирования общего назначения, python универсален, поэтому подходит для решения разнообразных задач.'''
Применю к нему паттерн, в котором в первых скобках до точки нахожу целое слово в верхнем регистре, во вторых скобках нахожу слова в нижнем регистре и ссылаюсь на первую группу, игнорируя регистр.
pattern = r'(\b[А-ЯA-Z]+\b).*\s*(?=\b[а-яa-z]+\b)(?i)\1'
re.findall(pattern, text)
В результате работы паттерна в тексте нашлось слово «PYTHON»:
Условные операторы в регулярных выражениях
Изучая информацию по использованию условного оператора, я провел много времени не только в поиске самой структуры, но и в поиске правильного его использования. Это все исходит от того, что разные языки программирования поддерживают разные методы, и использование условного оператора с несколькими условиями являлось для меня сложной задачей. Обходным решением было использование стандартной конструкции if-then-else.
Для демонстрации использования условного оператора создадим паттерн:
pattern = r'\b(P)?(?(1)YTHON|Python)'
Следует немного пояснить работу паттерна. Создается группа №1, если слово начинается с заглавной буквы «P». Конструкция ?(1) проверяет существует ли группа №1. Если группа №1 существует, запускаются 2 сценария. Первый проверяет остальную часть слова на соответствие «YTHON», если не соответствует, проверяется на слово «Python». Если какое-либо условие отрабатывается, возвращается совпадение с помощью метода match.
Если группы №1 не существует, слово пропускается.
Проверю работу паттерна на примере:
text = r'''PYTHON (питОн или пАйтон) - высокоуровневый язык программирования общего назначения. Python универсален, поэтому подходит для решения разнообразных задач.'''
pattern = r'\b(P)?(?(1)YTHON|Python)'
for match in re.finditer(pattern, text):
print(match.group(), end=' ')
На вывод получил ожидаемые слова:
Черный список слов
Часто в работе по поиску нужных слов в неструктурируемом тексте возникает потребность в формировании списка слов, которые мне заведомо не пригодятся.
Для этого возьму текст, который использовал в предыдущем примере:
text = r'''PYTHON (питОн или пАйтон) - высокоуровневый язык программирования общего назначения. Python универсален, поэтому подходит для решения разнообразных задач.'''
И составлю паттерн:
pattern = r'\b(?:язык|программирования|решения)\b|([\w\. \(\),-]+)'
В паттерне две группы и условие ИЛИ между ними, в результате которого возвращается либо None, либо ответ, подходящий под одно из условий. Первое условие со скобочной группой (?:…), которая создает группу №1, если слово из текста совпадет с одним из слов списка, но не сохранит результат т.к. шаблон (?:…) группирует, но не сохраняет результат. В случае успешного выполнения ответом будет пустое значение. Если слово не подходит под первое условие, то выполнится второе условие, под которое подходит слово, состоящее из любых символов, цифр и знаков точки, запятой, скобок и тире. В нашем тексте первым словом будет «PYTHON».
В результате метод findall с условием ИЛИ создаст группы из слов текста и выдаст результат, в котором не будет ненужных нам слов.
print(*re.findall(pattern, text))
Результат работы регулярного выражения:
При использовании регулярных выражений погружаешься в них все глубже, и, приобретая опыт, паттерны становятся эффективнее, большие алгоритмы обработки текста становятся проще и изящнее. Никогда не прекращайте развиваться, и я желаю вам удачи в получении новых знаний.
Для тех, кто хочет немного потренироваться в работе с регулярными выражениями в игровой форме рекомендую сайт regexcrossword.com