Флаги и проверки в регулярных выражениях
Рассматриваемые ранее примеры регулярных выражений имеют один существенный недостаток: они способны находить соответствия там, где это не предполагалось. Например, выражение вида:
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