Что делает выражение t python
Перейти к содержимому

Что делает выражение t python

  • автор:

Флаги и проверки в регулярных выражениях

Рассматриваемые ранее примеры регулярных выражений имеют один существенный недостаток: они способны находить соответствия там, где это не предполагалось. Например, выражение вида:

import re text = "подоходный налог" match = re.findall(r"прибыль|обретение|доход", text) print(match)

найдет подстроку «доход» в слове «подоходный». Подобные моменты как раз и решаются с помощью проверок. В данном случае для выделения слова «доход» целиком можно воспользоваться проверкой \b – граница слова. Фактически, это набор небуквенных и нецифровых символов. Запишем регулярное выражение в виде:

match = re.findall(r"прибыль|обретение|\bдоход\b", text)

Теперь слово «подоходный» пропущено и на выходе получаем пустую коллекцию. Но, если добавим в текст это слово:

text = "подоходный налог, доход"

то оно будет успешно найдено. Вот пример простейшей проверки. Если нужно для всех трех вариантов выполнять такую проверку, то это можно записать так:

match = re.findall(r"\bприбыль\b|\bобретение\b|\bдоход\b", text)

или проще, с помощью группировки вариантов:

match = re.findall(r"\b(?:прибыль|обретение|доход)\b", text)

Во втором случае мы используем несохраняющую группировку и для найденных совпадений выполняем проверку на соответствие границы слова.

Обратите внимание, проверки не являются частью совпадения строки по шаблону, они лишь проверяют определенные условия, поэтому сам по себе символ \b в строке text не ищется, а определяется граница слова в шаблоне, где он записан.

В общем случае, для регулярных выражений доступны следующие проверки:

Рассмотрим примеры использования этих проверок. Предположим, у нас имеется вот такой многострочный текст:

text = """     Уроки по Python     """

И мы хотим выделить содержимое тега script. Запишем регулярное выражение в таком виде:

match = re.findall(r"^([\w\W]+)(?=)", text, re.MULTILINE)

Здесь сначала проверяется, что тег script записан вначале строки (флаг MULTILINE указывает, чтобы проверка ^ соответствовала началу каждой строки). Далее, проверяется запись тега script: он может быть с параметрами, а может идти и без параметров. Затем, выбираются все символы (любые), пока не встретится закрывающий тег script. В результате, получим:

[«\nlet o = document.getElementById(‘id_div’);\nconsole.log(obj);\n»]

Здесь может возникнуть вопрос: почему мы используем именно такой символьный класс [\w\W] для выбора всех символов? Почему бы здесь не использовать точку, которая соответствует любому символу. Дело в том, что точка соответствует любому символу, но не символу перевода строки \n. Поэтому вот такое выражение:

match = re.findall(r"^(.+)(?=)", text, re.MULTILINE)

даст пустую коллекцию, т.к. символ \n встречается сразу же после тега script.

Теперь посмотрим на первый символ ^ — начало строки. Если поставить хотя бы один пробел перед открывающим тегом script, то шаблон не сработает и получим пустую коллекцию. Это, как раз, из-за этой первой проверки.

Далее, если убрать проверку (?=), то будут выбраны все символы до конца текста:

[«\nlet o = document.getElementById(‘id_div’);\nconsole.log(obj);\n\n\n«]

Вернем ее и смотрите, эта проверка опережающая и закрывающий тег не входит в результат обработки. А вот если написать так:

match = re.findall(r"^([\w\W]+)(?)", text, re.MULTILINE)

то тег будет находиться в итоговой строке:

[«\nlet o = document.getElementById(‘id_div’);\nconsole.log(obj);\n«]

По аналогии работают и обратные проверки:

Предположим, мы хотим выбрать все пары:

Это можно сделать с помощью такого правила:

match = re.findall(r"([-\w]+)[ \t]*=[ \t]*[\"']([^\"']+)(?\t])", text, re.MULTILINE)

Первая скобка выделяет имя атрибута, после него должно идти равно (с пробелами или табуляциями или без них), далее, кавычка (двойная или одинарная) и в кавычках мы выбираем все символы не кавычки. В конце стоит негативная ретроспективная проверка на отсутствие в конце проверяемой строки пробела или табуляции. Она будет отсекать эти символы, если они будут идти непосредственно перед закрывающей кавычкой. Результат работы данного правила, следующий:

[(‘http-equiv’, ‘Content-Type’), (‘content’, ‘text/html; charset=windows-1251’), (‘name’, ‘viewport’), (‘content’, ‘width=device-width, initial-scale=1.0’), (‘type’, ‘text/javascript’)]

Как видите, получили список кортежей для пар ключ=значение. Мало того, если перед закрывающей кавычкой у значения поставить пробелы или табуляции, то они будут проигнорированы.

Но здесь есть тонкий момент. Если поставить символы кавычек после ретроспективной проверки:

match = re.findall(r"([-\w]+)[ \t]*=[ \t]*[\"'](.+?)(?\t])[\"']", text, re.MULTILINE)

то мы получим не то, что ожидаем:

[(‘http-equiv’, ‘Content-Type » content=’), (‘name’, ‘viewport’), (‘content’, ‘width=device-width, initial-scale=1.0’), (‘type’, ‘text/javascript’)]

У нас второе значение будет захватывать следующую пару ключ=значение, так как мы здесь попросту нарушили шаблон. В первом случае правило могло не учитывать последние пробелы и завершить шаблон при закрывающей кавычке, т.к. мы явно не требовали ее наличия в конце. Здесь же они явно указаны и при остановке на пробелах кавычек уже не будет в конце и шаблон становится некорректным.

Подобные моменты особенно часто встречаются на начальном этапе использования регулярных выражений. В них нужно стараться вникать, разбираться и в последующем не совершать похожих ошибок. И, как всегда, здесь все приходит с опытом. Благо на просторах Интернета много примеров использования регулярных выражений и на всем этом богатстве материала можно быстро освоить хотя бы начальный минимум.

Давайте теперь немного усложним задачу и определим правило для выделения пар:

ключ=»значение» или ключ=значение

Например, в тексте ниже есть ключ align=center, значение которого записано без кавычек.

text = """     Уроки по Python  

Hello World!

"""

Для решения данной задачи воспользуемся еще одним типом проверки. В Python можно проверять наличие группирующего выражения, например:

Мы здесь находим и сохраняем кавычки с именем группы q. Затем, в зависимости от наличия или отсутствия этой группы, выполнять заданный шаблон. Для этого используется такой синтаксис:

Здесь yes_pattern – шаблон, выполняемый при наличии группы; no_pattern – шаблон, выполняемый при отсутствии группы.

Возвращаясь к нашей задаче, определим такое регулярное выражение:

match = re.findall(r"([-\w]+)[ \t]*=[ \t]*(?P[\"'])?(?(q)([^\"']+(?\t]))|([^ \t>]+))", text, re.MULTILINE)

Смотрите, мы здесь проверяем: если кавычки присутствуют, то выполняем прежний шаблон, а иначе, новый с выбором всех символов, пока не встретится пробел, табуляция или закрывающаяся скобка. При запуске увидим следующий результат:

[(‘http-equiv’, ‘»‘, ‘Content-Type’, »), (‘content’, ‘»‘, ‘text/html; charset=windows-1251’, »), (‘name’, ‘»‘, ‘viewport’, »), (‘content’, ‘»‘, ‘width=device-width, initial-scale=1.0’, »), (‘align’, », », ‘center’)]

У нас здесь был успешно выделен атрибут align со значением center благодаря использованию проверки группы с кавычками.

В заключение этого занятия я приведу набор флагов, которые можно назначать к регулярным выражениям:

Все эти флаги достаточно очевидны, кроме, может быть, последнего. Давайте воспользуемся им и запишем наше последнее правило с набором комментариев:

match = re.findall(r"""([-\w]+) #выделяем атрибут [ \t]*=[ \t]* #далее, должно идти равно и кавычки (?P[\"'])? #проверяем наличие кавычки (?(q)([^\"']+(?\t]))|([^ \t>]+)) #выделяем значение атрибута """, text, re.MULTILINE|re.VERBOSE)

Смотрите, мы здесь записали правило в несколько строк, добавили комментарии и объединили два флага: MULTILINE и VERBOSE, используя операцию логического ИЛИ.

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

  • a – то же самое, что и re.ASCII;
  • i – соответствует re.IGNORECASE;
  • m – для re.MULTILINE;
  • s – для re.DOTALL;
  • x – для re.VERBOSE.
text = "Python, python, PYTHON" match = re.findall(r"(?im)python", text) print(match)

Мы здесь включили два флага: re.IGNORECASE и re.MULTILINE. Благодаря первому, в строке находятся все три совпадения со словом python: [‘Python’, ‘python’, ‘PYTHON’] Вот так реализуются проверки и флаги в регулярных выражениях на Python. И мы с вами охватили весь материал по основам построения регулярных выражений. Используя эти знания, можно создавать правила для обработки строк в самых разных задачах. Но как это реализовать, используя методы модуля re, мы с вами подробно поговорим на следующем занятии.

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

Несколько регулярных выражений можно склеивать между собой, чтобы сформировать новые выражения.
Например, если A и B являются регулярными выражениями, тогда AB тоже является регулярным выражением.

В общем случае, если строка p подпадает под шаблон A , а другая строка q подпадает под шаблон B , то строка pq подпадёт под AB . Исключение составляют случаи, когда :
1. A или B содержат условия;
2. в A или B используются пограничные описатели ( \b );
3. A и/или B содержат нумерованные группы.

На заметку

Сложные выражения могут быть составлены из более простых. За более подробной информацией о регулярных выражениях можно обратиться к первому изданию книги Mastering Regular Expressions (Джеффри Фридл, русское издание — «Регулярные выражения»), или практически к любой другой книге об устройстве компиляторов.

Введение

Регулярные выражения могут содержать как специальные, так и обычные символы. Обычные символы, например, A , a , 0 , уже можно расценивать, как простейшие регулярные выражения — под эти шаблоны подпадают собственно строки, состоящие из этих символов. Если склеить несколько символов, например, в шаблон cat , то под этот шаблон подпадёт строка cat .

Некоторые символы имеют специальное значение и потому называются специальными (пример: | , ( ). Обычные такие символы либо описывают целый класс обычных символов, либо воздействуют на то, как интерпретируется регулярное выражение.

Специальные символы
Базовые
\ Либо экранирует специальные символы, либо указывает на использование специальной последовательности (см. ниже). Помните: в Питоне обратный слеш тоже используется для экранирования в строковых литералах, поэтому зачастую в шаблонах рекомендуется использовать сырые строки (с префиксом r ), которые не требуют экранирования.
. По умолчанию описывает любой символ, кроме перевода строки. С флагом DOTALL — также и перевод строки.
^ Начало строки. С флагом MULTILINE — начало каждой из строк.
$ Завершение строки. С флагом MULTILINE — завершение каждой из строк.
x|y Описывает первое подходящее из указанных выражений x или y . При помощи | можно соединить и более двух выражений.
[] Описывает указанное в скобках множество допустимых символов. Специальные символы внутри множества не работают. Поддерживаются диапазоны, например: [a-z] , [Н-Я] , [0-8] . Описатели классов символов допускаются, но зависят от режима интерпретации выражения ( ASCII , либо LOCALE ). Описать символы не входящие в указанное множество можно при помощи ^ после открывающей скобки, например: [^abc] , [^\d] . Чтобы включить символ ] во множество, его можно либо указать непосредственно после открывающей скобки, либо экранировать при помощи \ .
Специальные последовательности (начинаются с обратного слеша — \ )
\num Где num — число (от 1 до 99). Ссылается на группу указанную при помощи её порядкового номера. Под группой с индексом 0 обычно понимается обнаруженная строка в целом.
\A Пустая строка в начале строки.
\b Пограничная пустая строка (в начале или конце слова). Определение слова может зависеть от текущей локали. Внимание: в диапазонах символизирует возврат на символ (backspace).
\B Пустая строка не в начале и не в конце слова (т.е. обычно внутри слова).
\d Описывает десятичную цифру. Для Юникода — это символы категории [Nd] , для ASCII и байт — это то же, что и [0-9] .
\D Нецифровой символ.
\s Пробельные символы. Для ASCII — то же, что и [ \t\n\r\f\v] , для Юникода включает дополнительные символы, например неразрывный пробел.
\S Непробельные символы.
\w Символы, которые могут входить в состав слов, включая числа и подчёркивание. Для ASCII и байт — то же, что и [a-zA-Z0-9_] .
\W Несловарные символы.
\Z Пустая строка в конце строки.
Количественные описатели
* 0 и более повторений предшествующего выражения.
+ 1 и более повторений предшествующего выражения.
? 0 или 1 повторение предшествующего выражения.
x (число) повторений предшествующего выражения.
От x до y повторений предшествующего выражения. Если опустить x , то предполагается 0; если опустить y , то предполагается неопределённое количество (запятую при этом опускать нельзя).
+py3.11 + Притяжательные количественные описатели. Сравните: под aaa подпадает aaaaaa (под a подходят первые 4 символа), в то время как при использовании a+aa соответствие найдено не будет (найдутся пять символов, но за ними не будет aa . x+ то же, что и (?>x) .
*? , +? , ?? , ? Добавление ? к указанным описателям делает их нежадными, т.е. они станут описывать как можно меньшее количество символов. Сравните: под <.+>подпадёт вся строка b , в то время, как под <.+?>— только .
+py3.11 *+ , ++ , ?+ Притяжательные количественные описатели. Сравните: под aa подпадает aaaa (под a подходят первые три символа), в то время как при использовании a+a соответствие найдено не будет (под a подходят все четрые символа, но за ними нет ещё одной a ). x*+ , x++ , x?+ то же что и (?>x*) , (?>x+) , (?>x?) соответственно.
Группировка
(x) Задаёт группу, в которую должно попасть выражение x , к которой можно обращаться в дальнейшем по индексу — \1 (если группа имеет номер 1).
(?Px) Задаёт группу с именем name , в которую должно попасть выражение x . Групп в дальнейшем можно адресовать как по указанному имени, так и по индексу — (?P=name) или \1 (если группа имеет номер 1).
(?:x) Задаёт группу, в которую должно попасть выражение x , к которой нельзя будет обратиться в дальнейшем ни по индексу, ни по имени.
(?#. ) Комментарий. При разборе выражения будет проигнорирован.
Условия
x(?=y) Описывает выражение x только если за ним следует y . Например: маршал (?=Жуков) обнаружит маршал в строке маршал Жуков , но не в маршал Конев .
x(?!y) Описывает выражение x только если за ним не следует y . Например: маршал (?!Жуков) обнаружит маршал в строке маршал Конев , но не в маршал Жуков .
x(?<=y) Описывает выражение y только если ему предшествует x . Например: (?<=маршал) Жуков обнаружит Жуков в строке маршал Жуков , но не в капитан Жуков . Внимание: использование количественных описателей не поддерживается, требуются строки фиксированной длины.
x(? Описывает выражение y только если ему не предшествует x . Например: (?Внимание: использование количественных описателей не поддерживается, требуются строки фиксированной длины.
+py2.4 (?(g)y|n) Если группа g обнаружена, то описывает выражение y , иначе — выражение n . Например: под (?Pтоварищ)?(?(is_comrade) и брат|тамбовский волк) подпадают строки товарищ и брат и тамбовский волк .
Управление интерпретацией
(?aiLmsux) Позволяет задать флаги интерпретации всего выражения без передачи параметра flag функциям. Значения символов: a — только ASCII; i — игнорировать регистр; L — использовать локаль; m — многострочный режим; s — точка обозначает любой символ; u — Юникод; x — дозволить описания в выражении.
+py3.6 (?aiLmsux-imsx. ) Позволяет задать флаги интерпретации указанного выражения. После минуса можно указать флаги, которые требуется сбросить. a , L , u — взаимоисключающие,
Флаги интерпретации
+py3.0 re.A , re.ASCII Если установлен, то \w , \W , \b , \B , \d , \D , \s и \S описывают только ASCII символы, а не Юникод. Внимание: для байтовых шаблонов не используется.
re.DEBUG Выводить отладочную информацию о скомпилированном выражении.
re.I , re.IGNORECASE Игнорировать регистр символов. Текущая локаль не влияет на работу, покуда не проставлен re.LOCALE .
re.L , re.LOCALE Если установлен, то \w , \W , \b , \B будут ориентироваться на настройки текущей локали. Рассчитан на работу с байтами. В +py3.0 использование не рекомендуется — работа с Юникодом даёт более надёжные результаты.
re.M , re.MULTILINE Многострочный режим. Если установлен, то ^ и $ будут находить начало и конец соответственно для каждой строки, включая переводы строк. В про время как по умолчанию на переводы строк указанные описатели не реагируют.
re.S , re.DOTALL Если установлен, то . обозначает любой символ, включая перевод строки.
+py2.0 re.U , re.UNICODE Если установлен, то \w , \W , \b , \B , \d , \D , \s и \S описывают символы Юникода. Внимание: для +py3.0 это режим по умолчанию.
re.X , re.VERBOSE Дозволяет описания в регулярных выражениях. Благодаря описаниям иногда шаблоны становятся более читаемыми. Описание можно поместить прямо в шаблон, начав его с # , при этом неэкранированные пробелы в шаблоне игнорируются.
 import re

# Компилируем выражение для последующего использования.
# Совместим два флага re.X и re.DEBUG
re_test = re.compile(r'''
(\d)\s # год
кот # просто кот
''', re.X | re.DEBUG)

# Строка '2018 кот' соответствует выражению.
# Получим содержимое первой группы (год).
re_test.match('2018 кот').group(1) # 2018

# Строка '2018 год' не соответствует выражению.
re_test.match('2018 год') # None

Примеры применения регулярных выражений в Python

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

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

Регулярные выражения, также называемые regex, используются практически во всех языках программирования. В python они реализованы в стандартном модуле re .
Он широко используется в естественной обработке языка, веб-приложениях, требующих проверки ввода текста (например, адреса электронной почты) и почти во всех проектах в области анализа данных, которые включают в себя интеллектуальную обработку текста.

Эта статья разделена на 2 части.

Прежде чем перейти к синтаксису регулярных выражений, для начала вам лучше понять, как работает модуль re .

Итак, сначала вы познакомитесь с 5 основными функциями модуля re , а затем посмотрите, как создавать регулярные выражения в python.
Узнаете, как построить практически любой текстовый шаблон, который вам, скорее всего, понадобится при работе над проектами, связанными с поиском текста.

Что такое шаблон регулярного выражения и как его скомпилировать?

Шаблон регулярного выражения представляет собой специальный язык, используемый для представления общего текста, цифр или символов, извлечения текстов, соответствующих этому шаблону.

Основным примером является \s+ .
Здесь \ s соответствует любому символу пробела. Добавив в конце оператор + , шаблон будет иметь не менее 1 или более пробелов. Этот шаблон будет соответствовать даже символам tab \t .

В конце этой статьи вы найдете больший список шаблонов регулярных выражений. Но прежде чем дойти до этого, давайте посмотрим, как компилировать и работать с регулярными выражениями.

>>> import re >>> regex = re.compile('\s+') 

Вышеупомянутый код импортирует модуль re и компилирует шаблон регулярного выражения, который соответствует хотя бы одному или нескольким символам пробела.

Как разбить строку, разделенную регулярным выражением?

Рассмотрим следующий фрагмент текста.

>>> text = """100 ИНФ Информатика 213 МАТ Математика 156 АНГ Английский""" 

У меня есть три курса в формате “[Номер курса] [Код курса] [Название курса]”. Интервал между словами разный.

Передо мной стоит задача разбить эти три предмета курса на отдельные единицы чисел и слов. Как это сделать?
Их можно разбить двумя способами:

  • Используя метод re.split .
  • Вызвав метод split для объекта regex .
# Разделит текст по 1 или более пробелами >>> re.split('\s+', text) # или >>> regex.split(text) ['100', 'ИНФ', 'Информатика', '213', 'МАТ', 'Математика', '156', 'АНГ', 'Английский'] 

Оба эти метода работают. Но какой же следует использовать на практике?
Если вы намерены использовать определенный шаблон несколько раз, вам лучше скомпилировать регулярное выражение, а не использовать re.split множество раз.

Поиск совпадений с использованием findall, search и match

Предположим, вы хотите извлечь все номера курсов, то есть 100, 213 и 156 из приведенного выше текста. Как это сделать?

Что делает re.findall()?

#найти все номера в тексте >>> print(text) 100 ИНФ Информатика 213 МАТ Математика 156 АНГ Английский >>> regex_num = re.compile('\d+') >>> regex_num.findall(text) ['100', '213', '156'] 

В приведенном выше коде специальный символ \ d является регулярным выражением, которое соответствует любой цифре. В этой статье вы узнаете больше о таких шаблонах.
Добавление к нему символа + означает наличие по крайней мере 1 числа.

Подобно + , есть символ * , для которого требуется 0 или более чисел. Это делает наличие цифры не обязательным, чтобы получилось совпадение. Подробнее об этом позже.

В итоге, метод findall извлекает все вхождения 1 или более номеров из текста и возвращает их в список.

re.search() против re.match()

Как понятно из названия, regex.search() ищет шаблоны в заданном тексте.
Но, в отличие от findall , который возвращает согласованные части текста в виде списка, regex.search() возвращает конкретный объект соответствия. Он содержит первый и последний индекс первого соответствия шаблону.

Аналогично, regex.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('[А-ЯЁ]', text) # извлечь все названия курсов >>> re.findall('[а-яА-ЯёЁ]', text) ['100', '213', '156'] ['ИНФ', 'МАТ', 'АНГ'] ['Информатика', 'Математика', 'Английский'] 

Давайте посмотрим, что получилось.
Я скомпилировал 3 отдельных регулярных выражения по одному для соответствия номерам курса, коду и названию.
Для номера курса, шаблон [0-9]+ указывает на соответствие всем числам от 0 до 9. Добавление символа + в конце заставляет найти по крайней мере 1 соответствие цифрам 0-9. Если вы уверены, что номер курса, будет иметь ровно 3 цифры, шаблон мог бы быть [0-9] .

Для кода курса, как вы могли догадаться, [А-ЯЁ] будет совпадать с 3 большими буквами алфавита А-Я подряд (буква “ё” не включена в общий диапазон букв).

Для названий курса, [а-яА-ЯёЁ] будем искать а-я верхнего и нижнего регистра, предполагая, что имена всех курсов будут иметь как минимум 4 символа.

Можете ли вы догадаться, каков будет шаблон, если максимальный предел символов в названии курса, скажем, 20?
Теперь мне нужно написать 3 отдельные строки, чтобы разделить предметы. Но есть лучший способ. Группы регулярных выражений.
Поскольку все записи имеют один и тот же шаблон, вы можете создать единый шаблон для всех записей курса и внести данные, которые хотите извлечь из пары скобок ().

# создайте группы шаблонов текста курса и извлеките их >>> course_pattern = '([0-9]+)\s*([А-ЯЁ])\s*([а-яА-ЯёЁ])' >>> re.findall(course_pattern, text) [('100', 'ИНФ', 'Информатика'), ('213', 'МАТ', 'Математика'), ('156', 'АНГ', 'Английский')] 

Обратите внимание на шаблон номера курса: [0-9]+ , код: [А-ЯЁ] и название: [а-яА-ЯёЁ] они все помещены в круглую скобку (), для формирования группы.

Что такое “жадное” соответствие в регулярных выражениях?

По умолчанию, регулярные выражения должны быть жадными. Это означает, что они пытаются извлечь как можно больше, пока соответствуют шаблону, даже если требуется меньше.

Давайте рассмотрим пример фрагмента HTML, где нам необходимо получить тэг HTML.

>>> text = "Пример жадного соответствия регулярных выражений" >>> re.findall('<.*>', text) ['Пример жадного соответствия регулярных выражений'] 

Вместо совпадения до первого появления ‘>’, которое, должно было произойти в конце первого тэга тела, он извлек всю строку. Это по умолчанию “жадное” соответствие, присущее регулярным выражениям.

С другой стороны, ленивое соответствие “берет как можно меньше”. Это можно задать добавлением ? в конец шаблона.

>>> re.findall('<.*?>', text) ['', ''] 

Если вы хотите получить только первое совпадение, используйте вместо этого метод поиска search .

re.search('<.*?>', text).group() '' 

Наиболее распространенный синтаксис и шаблоны регулярных выражений

Теперь, когда вы знаете как пользоваться модулем re, давайте рассмотрим некоторые обычно используемые шаблоны подстановок.

Основной синтаксис

. Один символ кроме новой строки
\. Просто точка . , обратный слеш \ убирает магию всех специальных символов.
\d Одна цифра
\D Один символ кроме цифры
\w Один буквенный символ, включая цифры
\W Один символ кроме буквы и цифры
\s Один пробельный (включая таб и перенос строки)
\S Один не пробельный символ
\b Границы слова
\n Новая строка
\t Табуляция

Модификаторы

$ Конец строки
^ Начало строки
ab|cd Соответствует ab или de.
[ab-d] Один символ: a, b, c, d
[^ab-d] Любой символ, кроме: a, b, c, d
() Извлечение элементов в скобках
(a(bc)) Извлечение элементов в скобках второго уровня

Повторы

[ab]

2 непрерывных появления a или b
[ab]

от 2 до 5 непрерывных появления a или b
[ab]

2 и больше непрерывных появления a или b
+ одно или больше
* 0 или больше
? 0 или 1

Примеры регулярных выражений

Любой символ кроме новой строки

>>> text = 'python.org' >>> print(re.findall('.', text)) # Любой символ кроме новой строки ['p', 'y', 't', 'h', 'o', 'n', '.', 'o', 'r', 'g'] >>> print(re.findall('. ', text)) ['pyt', 'hon', '.or'] 

Точки в строке

>>>text = 'python.org' >>> print(re.findall('\.', text)) # соответствует точке ['.'] >>> print(re.findall('[^\.]', text)) # соответствует всему кроме точки ['p', 'y', 't', 'h', 'o', 'n', 'o', 'r', 'g'] 

Любая цифра

>>> text = '01, Янв 2018' >>> print(re.findall('\d+', text)) # Любое число (1 и более цифр подряд) ['01', '2018'] 

Все, кроме цифры

>>> text = '01, Янв 2018' >>> print(re.findall('\D+', text)) # Любая последовательность, кроме цифр [', Янв '] 

Любая буква или цифра

>>> text = '01, Янв 2018' >>> print(re.findall('\w+', text)) # Любой символ(1 или несколько подряд) ['01', 'Янв', '2018'] 

Все, кроме букв и цифр

>>> text = '01, Янв 2018' >>> print(re.findall('\W+', text)) # Все кроме букв и цифр [', ', ' '] 

Только буквы

>>> text = '01, Янв 2018' >>> print(re.findall('[а-яА-ЯёЁ]+', text)) # Последовательность букв русского алфавита ['Янв'] 

Соответствие заданное количество раз

>>> text = '01, Янв 2018' >>> print(re.findall('\d', text)) # Любые 4 цифры подряд ['2018'] >>> print(re.findall('\d', text)) ['01', '2018'] 

1 и более вхождений

>>> print(re.findall(r'Co+l', 'So Cooool')) # 1 и более буква 'o' в строке ['Cooool'] 

Любое количество вхождений (0 или более раз)

>>> print(re.findall(r'Pi*lani', 'Pilani')) ['Pilani'] 

0 или 1 вхождение

>>> print(re.findall(r'colou?r', 'color')) ['color'] 

Граница слова
Границы слов \b обычно используются для обнаружения и сопоставления началу или концу слова. То есть, одна сторона является символом слова, а другая сторона является пробелом и наоборот.

Например, регулярное выражение \btoy совпадает с ‘toy’ в ‘toy cat’, но не в ‘tolstoy’. Для того, чтобы ‘toy’ соответствовало ‘tolstoy’, используйте toy\b .
Можете ли вы придумать регулярное выражение, которое будет соответствовать только первой ‘toy’в ‘play toy broke toys’? (подсказка: \ b с обеих сторон)
Аналогично, \ B будет соответствовать любому non-boundary( без границ).
Например, \ Btoy \ B будет соответствовать ‘toy’, окруженной словами с обеих сторон, как в ‘antoynet’.

>>> re.findall(r'\btoy\b', 'play toy broke toys') # соедини toy с ограничениями с обеих сторон ['toy'] 

Практические упражнения

Давайте немного попрактикуемся. Пришло время открыть вашу консоль. (Варианты ответов здесь)

1. Извлеките никнейм пользователя, имя домена и суффикс из данных email адресов.

emails = """zuck26@facebook.com page33@google.com jeff42@amazon.com""" # требуеый вывод [('zuck26', 'facebook', 'com'), ('page33', 'google', 'com'), ('jeff42', 'amazon', 'com')] 

2. Извлеките все слова, начинающиеся с ‘b’ или ‘B’ из данного текста.

text = """Betty bought a bit of butter, But the butter was so bitter, So she bought some better butter, To make the bitter butter better.""" # требуеый вывод ['Betty', 'bought', 'bit', 'butter', 'But', 'butter', 'bitter', 'bought', 'better', 'butter', 'bitter', 'butter', 'better'] 

3. Уберите все символы пунктуации из предложения

sentence = """A, very very; irregular_sentence""" # требуеый вывод A very very irregular sentence 

4. Очистите следующий твит, чтобы он содержал только одно сообщение пользователя. То есть, удалите все URL, хэштеги, упоминания, пунктуацию, RT и CC.

tweet = '''Good advice! RT @TheNextWeb: What I would do differently if I was learning to code today https://t.co/lbwej0pxOd cc: @garybernhardt #rstats''' # требуеый вывод 'Good advice What I would do differently if I was learning to code today' 
  1. Извлеките все текстовые фрагменты между тегами с HTML страницы: https://raw.githubusercontent.com/selva86/datasets/master/sample.html
    Код для извлечения HTML страницы:
import requests r = requests.get("https://raw.githubusercontent.com/selva86/datasets/master/sample.html") r.text # здесь хранится html # требуеый вывод ['Your Title Here', 'Link Name', 'This is a Header', 'This is a Medium Header', 'This is a new paragraph! ', 'This is a another paragraph!', 'This is a new sentence without a paragraph break, in bold italics.'] 

Ответы

# 1 задание >>> pattern = r'(\w+)@([A-Z0-9]+)\.([A-Z])' >>> re.findall(pattern, emails, flags=re.IGNORECASE) [('zuck26', 'facebook', 'com'), ('page33', 'google', 'com'), ('jeff42', 'amazon', 'com')] 
# 2 задание >>> import re >>> re.findall(r'\bB\w+', text, flags=re.IGNORECASE) ['Betty', 'bought', 'bit', 'butter', 'But', 'butter', 'bitter', 'bought', 'better', 'butter', 'bitter', 'butter', 'better'] 

\b находится слева от ‘B’, значит слово должно начинаться на ‘B’.
Добавьте flags=re.IGNORECASE , что бы шаблон был не чувствительным к регистру.

# 3 задание >>> import re >>> " ".join(re.split('[;,\s_]+', sentence)) 'A very very irregular sentence' 
# 4 задание >>> import re >>> def clean_tweet(tweet): tweet = re.sub('http\S+\s*', '', tweet) # удалит URL tweet = re.sub('RT|cc', '', tweet) # удалит RT и cc tweet = re.sub('#\S+', '', tweet) # удалит хештеги tweet = re.sub('@\S+', '', tweet) # удалит упоминани tweet = re.sub('[%s]' % re.escape("""!"#$%&'()*+,-./:;?@[\]^_`<|>~"""), '', tweet) # удалит символы пунктуации tweet = re.sub('\s+', ' ', tweet) # заменит пробельные символы на 1 пробел return tweet >>> print(clean_tweet(tweet)) 'Good advice What I would do differently if I was learning to code today' 
# 5 задание >>> re.findall('<.*?>(.*)', r.text) ['Your Title Here', 'Link Name', 'This is a Header', 'This is a Medium Header', 'This is a new paragraph! ', 'This is a another paragraph!', 'This is a new sentence without a paragraph break, in bold italics.'] 

Надеемся, информация была вам полезна. Стояла цель — познакомить вас с примерами регулярных выражений легким и доступным для запоминания способом.

  • ТЕГИ
  • Регулярные выражения

Python RegEx: практическое применение регулярок

Разберём регулярные выражения в Python, их синтаксис, популярные методы специального модуля re, а также попрактикуемся на задачах.

Рассмотрим регулярные выражения в Python, начиная синтаксисом и заканчивая примерами использования.

Примечание Вы читаете улучшенную версию некогда выпущенной нами статьи.

  1. Основы регулярных выражений
  2. Регулярные выражения в Python
  3. Задачи

Основы регулярных выражений

Регулярками в Python называются шаблоны, которые используются для поиска соответствующего фрагмента текста и сопоставления символов.

Грубо говоря, у нас есть input-поле, в которое должен вводиться email-адрес. Но пока мы не зададим проверку валидности введённого email-адреса, в этой строке может оказаться совершенно любой набор символов, а нам это не нужно.

Чтобы выявить ошибку при вводе некорректного адреса электронной почты, можно использовать следующее регулярное выражение:

r'^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)+$' 

По сути, наш шаблон — это набор символов, который проверяет строку на соответствие заданному правилу. Давайте разберёмся, как это работает.

Синтаксис RegEx

Синтаксис у регулярок необычный. Символы могут быть как буквами или цифрами, так и метасимволами, которые задают шаблон строки:

Python RegEx: практическое применение регулярок 1

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

  • \d — соответствует любой одной цифре и заменяет собой выражение [0-9];
  • \D — исключает все цифры и заменяет [^0-9];
  • \w — заменяет любую цифру, букву, а также знак нижнего подчёркивания;
  • \W — любой символ кроме латиницы, цифр или нижнего подчёркивания;
  • \s — соответствует любому пробельному символу;
  • \S — описывает любой непробельный символ.

Для чего используются регулярные выражения

  • для определения нужного формата, например телефонного номера или email-адреса;
  • для разбивки строк на подстроки;
  • для поиска, замены и извлечения символов;
  • для быстрого выполнения нетривиальных операций.

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

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

Хотите узнать больше? Обратите внимание на статью о регулярках для новичков.

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

В Python для работы с регулярками есть модуль re . Его нужно просто импортировать:

import re 

А вот наиболее популярные методы, которые предоставляет модуль:

  • re.match()
  • re.search()
  • re.findall()
  • re.split()
  • re.sub()
  • re.compile()

Рассмотрим каждый из них подробнее.

Разработка на Python с нуля: роадмап программиста

re.match(pattern, string)

Этот метод ищет по заданному шаблону в начале строки. Например, если мы вызовем метод match() на строке «AV Analytics AV» с шаблоном «AV», то он завершится успешно. Но если мы будем искать «Analytics», то результат будет отрицательный:

import re result = re.match(r'AV', 'AV Analytics Vidhya AV') print result Результат:

Искомая подстрока найдена. Чтобы вывести её содержимое, применим метод group() (мы используем «r» перед строкой шаблона, чтобы показать, что это «сырая» строка в Python):

result = re.match(r'AV', 'AV Analytics Vidhya AV') print result.group(0) Результат: AV 

Теперь попробуем найти «Analytics» в данной строке. Поскольку строка начинается на «AV», метод вернет None :

result = re.match(r'Analytics', 'AV Analytics Vidhya AV') print result Результат: None 

Также есть методы start() и end() для того, чтобы узнать начальную и конечную позицию найденной строки.

result = re.match(r'AV', 'AV Analytics Vidhya AV') print result.start() print result.end() Результат: 0 2 

Эти методы иногда очень полезны для работы со строками.

re.search(pattern, string)

Метод похож на match() , но ищет не только в начале строки. В отличие от предыдущего, search() вернёт объект, если мы попытаемся найти «Analytics»:

result = re.search(r'Analytics', 'AV Analytics Vidhya AV') print result.group(0) Результат: Analytics 

Метод search() ищет по всей строке, но возвращает только первое найденное совпадение.

re.findall(pattern, string)

Возвращает список всех найденных совпадений. У метода findall() нет ограничений на поиск в начале или конце строки. Если мы будем искать «AV» в нашей строке, он вернет все вхождения «AV». Для поиска рекомендуется использовать именно findall() , так как он может работать и как re.search() , и как re.match() .

result = re.findall(r'AV', 'AV Analytics Vidhya AV') print result Результат: ['AV', 'AV'] 

re.split(pattern, string, [maxsplit=0])

Этот метод разделяет строку по заданному шаблону.

result = re.split(r'y', 'Analytics') print result Результат: ['Anal', 'tics'] 

В примере мы разделили слово «Analytics» по букве «y». Метод split() принимает также аргумент maxsplit со значением по умолчанию, равным 0. В данном случае он разделит строку столько раз, сколько возможно, но если указать этот аргумент, то разделение будет произведено не более указанного количества раз. Давайте посмотрим на примеры Python RegEx:

result = re.split(r'i', 'Analytics Vidhya') print result Результат: ['Analyt', 'cs V', 'dhya'] # все возможные участки. 
result = re.split(r'i', 'Analytics Vidhya',maxsplit=1) print result Результат: ['Analyt', 'cs Vidhya'] 

Мы установили параметр maxsplit равным 1, и в результате строка была разделена на две части вместо трех.

re.sub(pattern, repl, string)

Ищет шаблон в строке и заменяет его на указанную подстроку. Если шаблон не найден, строка остается неизменной.

result = re.sub(r'India', 'the World', 'AV is largest Analytics community of India') print result Результат: 'AV is largest Analytics community of the World' 

re.compile(pattern, repl, string)

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

pattern = re.compile('AV') result = pattern.findall('AV Analytics Vidhya AV') print result result2 = pattern.findall('AV is largest analytics community of India') print result2 Результат: ['AV', 'AV'] ['AV'] 

До сих пор мы рассматривали поиск определенной последовательности символов. Но что, если у нас нет определенного шаблона, и нам надо вернуть набор символов из строки, отвечающий определенным правилам? Такая задача часто стоит при извлечении информации из строк. Это можно сделать, написав выражение с использованием специальных символов. Вот наиболее часто используемые из них:

Python RegEx: практическое применение регулярок 2

Больше информации по специальным символам можно найти в документации для регулярных выражений в Python 3.

Перейдём к практическому применению Python регулярных выражений и рассмотрим примеры.

Задачи

Вернуть первое слово из строки

Сначала попробуем вытащить каждый символ (используя . )

result = re.findall(r'.', 'AV is largest Analytics community of India') print result Результат: ['A', 'V', ' ', 'i', 's', ' ', 'l', 'a', 'r', 'g', 'e', 's', 't', ' ', 'A', 'n', 'a', 'l', 'y', 't', 'i', 'c', 's', ' ', 'c', 'o', 'm', 'm', 'u', 'n', 'i', 't', 'y', ' ', 'o', 'f', ' ', 'I', 'n', 'd', 'i', 'a'] 

Для того, чтобы в конечный результат не попал пробел, используем вместо . \w .

result = re.findall(r'\w', 'AV is largest Analytics community of India') print result Результат: ['A', 'V', 'i', 's', 'l', 'a', 'r', 'g', 'e', 's', 't', 'A', 'n', 'a', 'l', 'y', 't', 'i', 'c', 's', 'c', 'o', 'm', 'm', 'u', 'n', 'i', 't', 'y', 'o', 'f', 'I', 'n', 'd', 'i', 'a'] 

Теперь попробуем достать каждое слово (используя * или + )

result = re.findall(r'\w*', 'AV is largest Analytics community of India') print result Результат: ['AV', '', 'is', '', 'largest', '', 'Analytics', '', 'community', '', 'of', '', 'India', ''] 

И снова в результат попали пробелы, так как * означает «ноль или более символов». Для того, чтобы их убрать, используем + :

result = re.findall(r'\w+', 'AV is largest Analytics community of India') print result Результат: ['AV', 'is', 'largest', 'Analytics', 'community', 'of', 'India'] 

Теперь вытащим первое слово, используя ^ :

result = re.findall(r'^\w+', 'AV is largest Analytics community of India') print result Результат: ['AV'] 

Если мы используем $ вместо ^ , то мы получим последнее слово, а не первое:

result = re.findall(r'\w+$', 'AV is largest Analytics community of India') print result Результат: [‘India’] 

Вернуть первые два символа каждого слова

Вариант 1: используя \w , вытащить два последовательных символа, кроме пробельных, из каждого слова:

result = re.findall(r'\w\w', 'AV is largest Analytics community of India') print result Результат: ['AV', 'is', 'la', 'rg', 'es', 'An', 'al', 'yt', 'ic', 'co', 'mm', 'un', 'it', 'of', 'In', 'di'] 

Вариант 2: вытащить два последовательных символа, используя символ границы слова ( \b ):

result = re.findall(r'\b\w.', 'AV is largest Analytics community of India') print result Результат: ['AV', 'is', 'la', 'An', 'co', 'of', 'In'] 

Вернуть домены из списка email-адресов

Сначала вернём все символы после «@»:

result = re.findall(r'@\w+', 'abc.test@gmail.com, xyz@test.in, test.first@analyticsvidhya.com, first.test@rest.biz') print result Результат: ['@gmail', '@test', '@analyticsvidhya', '@rest'] 

Как видим, части «.com», «.in» и т. д. не попали в результат. Изменим наш код:

result = re.findall(r'@\w+.\w+', 'abc.test@gmail.com, xyz@test.in, test.first@analyticsvidhya.com, first.test@rest.biz') print result Результат: ['@gmail.com', '@test.in', '@analyticsvidhya.com', '@rest.biz'] 

Второй вариант — вытащить только домен верхнего уровня, используя группировку — ( ) :

result = re.findall(r'@\w+.(\w+)', 'abc.test@gmail.com, xyz@test.in, test.first@analyticsvidhya.com, first.test@rest.biz') print result Результат: ['com', 'in', 'com', 'biz'] 

Извлечь дату из строки

Используем \d для извлечения цифр.

result = re.findall(r'\d-\d-\d', 'Amit 34-3456 12-05-2007, XYZ 56-4532 11-11-2011, ABC 67-8945 12-01-2009') print result Результат: ['12-05-2007', '11-11-2011', '12-01-2009'] 

Для извлечения только года нам опять помогут скобки:

result = re.findall(r'\d-\d-(\d)', 'Amit 34-3456 12-05-2007, XYZ 56-4532 11-11-2011, ABC 67-8945 12-01-2009') print result Результат: ['2007', '2011', '2009'] 

Задачи по Python для начинающих от Tproger и GeekBrains

Извлечь слова, начинающиеся на гласную

Для начала вернем все слова:

result = re.findall(r'\w+', 'AV is largest Analytics community of India') print result Результат: ['AV', 'is', 'largest', 'Analytics', 'community', 'of', 'India'] 

А теперь — только те, которые начинаются на определенные буквы (используя [] ):

result = re.findall(r'[aeiouAEIOU]\w+', 'AV is largest Analytics community of India') print result Результат: ['AV', 'is', 'argest', 'Analytics', 'ommunity', 'of', 'India'] 

Выше мы видим обрезанные слова «argest» и «ommunity». Для того, чтобы убрать их, используем \b для обозначения границы слова:

result = re.findall(r'\b[aeiouAEIOU]\w+', 'AV is largest Analytics community of India') print result Результат: ['AV', 'is', 'Analytics', 'of', 'India'] 

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

result = re.findall(r'\b[^aeiouAEIOU]\w+', 'AV is largest Analytics community of India') print result Результат: [' is', ' largest', ' Analytics', ' community', ' of', ' India'] 

В результат попали слова, «начинающиеся» с пробела. Уберем их, включив пробел в диапазон в квадратных скобках:

result = re.findall(r'\b[^aeiouAEIOU ]\w+', 'AV is largest Analytics community of India') print result Результат: ['largest', 'community'] 

Проверить формат телефонного номера

Номер должен быть длиной 10 знаков и начинаться с 8 или 9. Есть список телефонных номеров, и нужно проверить их, используя регулярки в Python:

li = ['9999999999', '999999-999', '99999x9999'] for val in li: if re.match(r'[8-9][0-9]', val) and len(val) == 10: print 'yes' else: print 'no' Результат: yes no no 

Разбить строку по нескольким разделителям

line = 'asdf fjdk;afed,fjek,asdf,foo' # String has multiple delimiters (";",","," "). result = re.split(r'[;,\s]', line) print result Результат: ['asdf', 'fjdk', 'afed', 'fjek', 'asdf', 'foo'] 

Также мы можем использовать метод re.sub() для замены всех разделителей пробелами:

line = 'asdf fjdk;afed,fjek,asdf,foo' result = re.sub(r'[;,\s]',' ', line) print result Результат: asdf fjdk afed fjek asdf foo 

Извлечь информацию из html-файла

Допустим, нужно извлечь информацию из html-файла, заключенную между

и

, кроме первого столбца с номером. Также будем считать, что html-код содержится в строке.

Пример содержимого html-файла:

1NoahEmma2LiamOlivia3MasonSophia4JacobIsabella5WilliamAva6EthanMia7MichaelEmily 

С помощью регулярных выражений в Python это можно решить так (если поместить содержимое файла в переменную test_str ):

result = re.findall(r'\d([A-Z][A-Za-z]+)([A-Z][A-Za-z]+)', test_str) print result Результат: [('Noah', 'Emma'), ('Liam', 'Olivia'), ('Mason', 'Sophia'), ('Jacob', 'Isabella'), ('William', 'Ava'), ('Ethan', 'Mia'), ('Michael', 'Emily')] 

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *