Сегодня поговорим о такой важной теме, как регулярные выражения. Это очень важная тема для современного программирования, и они есть абсолютно во всех языках программирования. Более того, они универсальны, и совместимы друг с другом. Поэтому если вы имели дело с регулярными выражениями в других языках разработки приложений, то освоить эту тему в Python будет значительно проще.
Регулярные выражения, еще называемые regex, представляют собой синтаксис или язык для поиска, получения данных и работы с текстовыми шаблонами. Он распространен в проектах, включающих проверку текста, обработку естественного языка и интеллектуальную его обработку.
- Регулярные выражения: основы
- Понятие шаблона регулярного выражения
- Разбитие разделенной регулярным выражением строки
- Использование findall, searchи matchдля поиска одинаковых фрагментов текста
- Использование re.findallдля поиска совпадений
- re.search() или re.match()?
- Использование регулярных выражений для замены фрагментов текста
- Что такое группы регулярных выражений?
- Выводы
Регулярные выражения: основы
Все языки программирования задействуют регулярные выражения. Ведь это удобно. В python они используются в модуле re, который распространен для обработки естественного языка, создания веб-приложений, требующих проверки ввода текста (электронной почты, как вариант), и почти во всех проектах анализа данных, подразумевающих интеллектуальный анализ текста.
Без них обойтись невозможно в современном программировании. Ведь разработчикам регулярно приходится сталкиваться с большими массивами данных. Когда информация умещается на нескольких строках, нет ничего сложного в том, чтобы найти тот объект, часть строки или цифру, которую нужно. Но когда дело доходит до того, что массивы информации занимают гигабайты на жестком диске, то необходимо найти более универсальное автоматизированное средство, которое позволяет реализовать эту задачу.
Прежде чем перейти к синтаксису регулярных выражений, вам сначала нужно лучше понять принцип работы модуля re. Итак, сперва вы ознакомитесь с 5 основными функциями этого модуля, затем узнаете, как создавать регулярные выражения в python. Узнайте, как создать почти любой текстовый шаблон, который вам понадобится в ходе создания проектов текстового поиска, прямо сейчас.
Понятие шаблона регулярного выражения
Регулярные выражения существуют в виде шаблонов. Это главный термин, который нам сегодня надо рассмотреть.
Шаблон регулярного выражения — это специальный язык, используемый для представления общего текста, чисел или символов и извлечения текста, соответствующего этому шаблону.
Основной пример — \s+.
Здесь \s заменяет какой-угодно знак текста, независимо от того, он цифровой, буквенный или символьный. При добавлении оператора + в конце в шаблоне будет не менее 1 или более пробелов. Этот шаблон будет соответствовать даже символам табуляции \t.
В конце этой статьи вы найдете более длинный список шаблонов регулярных выражений. Но прежде чем мы перейдем к этому, давайте посмотрим, как компилировать регулярные выражения и работать с ними.
>>> import re >>> regex = re.compile('\s+')
Приведенный выше код импортирует модуль re и компилирует шаблон регулярного выражения, соответствующий по крайней мере одному или нескольким пробельным символам.
Разбитие разделенной регулярным выражением строки
А теперь давайте возьмем в качестве примера следующий кусок текста.
>>> text = """100 ИНФ Информатика 213 МАТ Математика 156 АНГ Английский"""
Исходя из него становится понятно, что исходные данные следующие: есть три курса, которые записываются в таком формате: “[Номер курса] [Код курса] [Название курса]”. Обратите внимание, что интервал здесь между словами отличается.
Теперь необходимо все эти три предмета курса разбить на отдельные единицы чисел и слов. Что нужно сделать для этого? Есть ли способ быстро добиться поставленной задачи?
Есть два способа, какими эту задачу можно реализовать.
- Воспользовавшись методом re.split.
- Вызвав метод split для объекта регулярного выражения.
# Разделит текст по 1 или более пробелами >>> re.split('\s+', text) # или >>> regex.split(text) ['100', 'ИНФ', 'Информатика', '213', 'МАТ', 'Математика', '156', 'АНГ', 'Английский']
В принципе, каждый их этих методов можно использовать. Но какой из них более предпочтительный на деле? Если вы планируете несколько раз пользоваться одним и тем же шаблоном, все же рекомендуется компиляция регулярного выражения вместо использования метода split для объекта regex многократно.
А теперь давайте поговорим о том, как использовать регулярные выражения для поиска фрагментов текста, которые нужны в каждом конкретном случае.
Использование findall, search и match для поиска одинаковых фрагментов текста
Предположим, необходимо извлечь из приведенной выше последовательности строк все номера курсов. А именно, числа 100, 213 и 156. Что нужно сделать для этого?
Использование re.findall для поиска совпадений
Re.Findall – это одна из главных функций, которая позволяет реализовать целый спектр задач, стоящих перед разработчиками приложений на Python. Как же с ней работать правильно?
Давайте в качестве примера приведем следующий код.
#найти все номера в тексте >>> print(text) 100 ИНФ Информатика 213 МАТ Математика 156 АНГ Английский >>> regex_num = re.compile('\d+') >>> regex_num.findall(text) ['100', '213', '156']
В этом фрагменте обратите внимание на специальный символ \d, который обозначает любую цифру. Сегодня вы с подобными шаблонами еще встретитесь.
Если добавить к нему символ +, то речь пойдет о по крайней мере одном числе.
Точно так же, как и для +, есть знак *, означающий по крайней мере 0 чисел. Простыми словами, с помощью звездочки мы делаем наличие цифры необязательным для того, чтобы интерпретатор посчитал, что произошло совпадение.
В общем, метод findall используется для извлечения всех вхождений номеров из текста и возврата их в список.
re.search() или re.match()?
Как можно догадаться из названия, re.search также осуществляет поиск совпадения. Так в чем же тогда разница между ними. Давайте сейчас разберемся в этом.
В отличие от функции re.findall, эта возвращает конкретный объект соответствия, а не список всех вхождений. Он включает первый и последний индекс первого соответствия шаблону.
regex.match также возвращает объект соответствия. В связи с этим, появляется вопрос: какая же разница между ними? Все дело в том, что re.match нужно, чтобы шаблон располагался непосредственно в начале текста.
>>> # создайте переменную с текстом >>> text2 = """ИНФ Информатика 213 МАТ Математика 156""" >>> # скомпилируйте regex и найдите шаблоны >>> regex_num = re.compile('\d+') >>> s = regex_num.search(text2) >>> print('Первый индекс: ', s.start()) >>> print('Последний индекс: ', s.end()) >>> print(text2[s.start():s.end()]) Первый индекс: 17 Последний индекс: 20 213
Аналогичный результат можно получить в качестве альтернативы, если использовать метод group() для объекта соответствия.
>>> print(s.group()) 205 >>> m = regex_num.match(text2) >>> print(m) None
Использование регулярных выражений для замены фрагментов текста
Изменение текста реализуется с использованием regex.sub(). Давайте рассмотрим такую измененную версию текста курсов. После каждого кода курса здесь реализована табуляция. Посмотрите, как это выглядит.
# создайте переменную с текстом >>> text = """100 ИНФ \t Информатика 213 МАТ \t Математика 156 АНГ \t Английский""" >>> print(text) 100 ИНФ Информатика 213 МАТ Математика 156 АНГ Английский
А теперь, предположим, нужно убрать все ненужные пробелы и записать все слова в форме одной строки. Как это сделать, чтобы не нарушить хрупкий код приложения?
Достаточно просто использовать regex.sub, чтобы заменить шаблон \s+ на один пробел.
# заменить один или больше пробелов на 1 >>> regex = re.compile('\s+') >>> print(regex.sub(' ', text))
либо
>>> print(re.sub('\s+', ' ', text)) 101 COM Computers 205 MAT Mathematics 189 ENG English
Допустим, необходимо избавиться от ненужных пробелов и записи курса выводить с новой строки. Для этого используйте регулярное выражение, которое пропускает новую строку, но включает любые остальные пробелы.
Это делается с использованием отрицательного совпадения (?!\n). Шаблон проверяет наличие новой строки (в python это \n), и пропускает ее.
# убрать все пробелы кроме символа новой строки >>> regex = re.compile('((?!\n)\s+)') >>> print(regex.sub(' ', text)) 100 ИНФ Информатика 213 МАТ Математика 156 АНГ Английский
Что такое группы регулярных выражений?
Под группами регулярных выражений подразумеваются специальные функции, которые дают возможность получать требуемые элементы в виде отдельных объектов соответствия.
Допустим, нам надо в качестве отдельных элементов получить номер курса, код и имя. Что делать для этого, если нет групп?
>>> text = """100 ИНФ Информатика 213 МАТ Математика 156 АНГ Английский""" # извлечь все номера курсов >>> re.findall('[0-9]+', text) # извлечь все коды курсов (для латиницы [A-Z]) >>> re.findall('[А-ЯЁ]{3}', text) # извлечь все названия курсов >>> re.findall('[а-яА-ЯёЁ]{4,}', text) ['100', '213', '156'] ['ИНФ', 'МАТ', 'АНГ'] ['Информатика', 'Математика', 'Английский']
Давайте разберемся, что получилось в итоге.
Было скомпилировано 3 отдельных регулярных выражения по одному для соответствия номерам курса, коду и названию. Для номера курса, шаблон [0-9] говорит о соответствии всем числам от 0 до 9. Если добавить символ + в конец, мы можем найти хотя бы одно соответствие цифрам 0 и 9. Если мы убеждены в том, что номер курса будет иметь ровно 3 цифры, то шаблон был бы таким.
[0-9] {3}
И, как можно было догадаться, для кода курса у [А-ЯЁ]{3} будет совпадение с тремя большими буквами алфавита А-Я подряд (при этом в общий список слов российская буква ё входить не будет).
Для названий курса [а-яА-ЯёЁ]{4,}, здесь осуществляется поиск а-я верхнего и нижнего регистра. При этом предполагается, что в названии всех курсов будет по крайней мере 4 символа.
А теперь давайте представим, что максимальный предел символов в названии курсов составляет 20 штук. Что тогда будет? Придется для разделения предметов писать 3 отдельные строки. Это неудобно. Значительно лучше воспользоваться группами регулярных выражений.
Так, как все записи имеют одинаковый шаблон, то можно просто создать общий для них всех и туда внести данные, которые хотите извлечь из пары скобок ().
На практике это реализуется так.
# создайте группы шаблонов текста курса и извлеките их >>> course_pattern = '([0-9]+)\s*([А-ЯЁ]{3})\s*([а-яА-ЯёЁ]{4,})' >>> re.findall(course_pattern, text) [('100', 'ИНФ', 'Информатика'), ('213', 'МАТ', 'Математика'), ('156', 'АНГ', 'Английский')]
Выводы
Мы разобрались в теме регулярных выражений. Конечно, в этом вопросе не все так просто. Например, есть огромное количество других шаблонов, предназначенных для поиска или замены того или иного символа или их совокупности.
Шаблоны регулярных выражений – это действенный инструмент, который способен существенно упростить жизнь как начинающего, так и опытного разработчика.
Попробуйте потренироваться и изменить коды программ, приведенные выше, под те или иные задачи. В процессе этого, вы научитесь более гибко работать с регулярными выражениями.