Шаг 74.
Однострочники Python. Регулярные выражения. Поиск простых комбинаций символов в строковых значениях. Общее описание

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

    Регулярное выражение (regular expression, или, сокращенно, regex) формально описывает поисковый шаблон (search pattern), на основе которого можно находить соответствующие части текста. Простой пример на рисунке 1 демонстрирует поиск слова Джульетта в тексте пьесы Шекспира "Ромео и Джульетта".


Рис.1. Поиск слова Джульетта в тексте пьесы Шекспира "Ромео и Джульетта"


В русскоязычной литературе часто встречается также термин "паттерн".

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

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

Регулярное выражение "точка"

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

import re

text = '''A blockchain, originally block chain,
is a growing list of records, called blocks,
which are linked using cryptography.
'''
print(re.findall('b...k', text))
# ['block', 'block', 'block']

    В этом примере используется метод findall() модуля re. Первый его аргумент - собственно, само регулярное выражение: мы ищем произвольную комбинацию символов, начинающуюся с символа 'b', за которым следуют три произвольных символа, ..., за которыми следует символ 'k'. Регулярному выражению b...k соответствует не только строка символов 'block', но и 'boook', 'b erk' и 'bloek'. Второй параметр метода findall() - текст, в котором производится поиск. Строковая переменная text содержит три подходящих шаблона символов, как видно из выведенных оператором print результатов.

Регулярное выражение "звездочка"

    Во-вторых, пусть требуется найти текст, который будет начинаться и заканчиваться символом 'y', с произвольным количеством символов посередине.

    Как это сделать? С помощью регулярного выражения "звездочка", то есть символа "*". В отличие от регулярного выражения "точка", регулярное выражение "звездочка" не является самостоятельным, а лишь модифицирует смысл других регулярных выражений. Рассмотрим следующий пример:

print(re.findall('y.*y', text))
# ['yptography']

    Оператор "звездочка" применяется к расположенному непосредственно перед ним регулярному выражению. В этом примере задаваемый регулярным выражением шаблон начинается с 'y', далее следует произвольное количество символов, ".*", за которыми снова следует символ 'y'. Как видите, слово 'cryptography' содержит одно вхождение этого шаблона: 'yptography'.

    Возможно, вы недоумеваете, почему этот код не находит длинную подстроку между 'originally' и 'cryptography', которая тоже вроде бы соответствует шаблону регулярного выражения y.*y. Дело в том, что оператор "точка" соответствует любому символу, кроме символа новой строки. В переменной text хранится многострочное строковое значение, включающее три символа новой строки. Оператор "звездочка" можно использовать и в сочетании с любым другим регулярным выражением. Например, регулярному выражению abc* соответствуют строки символов 'ab', 'abc', 'abcc' и 'abccdc'.

Регулярное выражение "один или ни одного"

    В-третьих, нужно уметь находить соответствие типа "один или ни одного" с помощью регулярного выражения, символа "?". Подобно оператору "*", знак вопроса модифицирует какое-либо другое регулярное выражение, как можно видеть из следующего примера:

print(re.findall('blocks?', text))
# ['block', 'block', 'blocks']

    Регулярное выражение "один или ни одного", "?", применяется к регулярному выражению, располагающемуся непосредственно перед ним, в данном случае к символу "s". Регулярное выражение "один или ни одного" означает, что модифицируемый им шаблон необязателен.

    В пакете re Python знак вопроса может использоваться и по-другому, но к регулярному выражению "один или ни одного" это отношения не имеет: знак вопроса в сочетании с оператором "звездочка", "*?", служит для "нежадного" (nongreedy) поиска соответствия шаблону. Например, при указании регулярного выражения ".*?" Python ищет минимальное количество произвольных символов. И наоборот, при указании оператора "звездочка" без знака вопроса он "жадно" ищет соответствие как можно большего количества символов.

    Рассмотрим пример. При поиске в строке HTML-кода '<div>hello world</div>' по регулярному выражению "<.*>" возвращается вся строка символов '<div>hello world</div>', а не только префикс '<div>'. Если же нужен только префикс, необходимо воспользоваться "нежадным" регулярным выражением "<.*?>":

txt = '<div>hello world</div>'

print(re.findall('<.*>', txt))
# ['<div>hello world</div>']

print(re.findall('<.*?>', txt))
# ['<div>', '</div>']

    Вооружившись знаниями этих трех инструментов - регулярных выражений "точка" ".", "звездочка" "*" и "один или ни одного" "?", - вы уже сможете разобраться в следующем однострочном решении.

    На следующем шаге мы закончим изучение этого вопроса.




Предыдущий шаг Содержание Следующий шаг