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

Метод find ищет подстроку в строке.

In [1]:
s='абракадабра'
s.find('бра')
Out[1]:
1

Если подстрока не найдена, он возвращает -1.

In [2]:
s.find('бро')
Out[2]:
-1

Можно задать дополнительный параметр - начиная с какого места искать.

In [3]:
s.find('бра',3)
Out[3]:
8

Метод replace возвращает копию строки, в кторой старая подстрока заменена на новую.

In [4]:
s.replace('бра','БРА')
Out[4]:
'аБРАкадаБРА'

Если задать дополнительный целый параметр n, то будут заменены только первые n вхождений старой подстроки.

In [5]:
s.replace('бра','БРА',1)
Out[5]:
'аБРАкадабра'

Но эти методы работают только с фиксированными подстроками. Для более гибкой обработки текстов можно использовать модуль re, который позволяет искать и заменять подстроки, соответствующий некоторым образцам - регулярным выражениям.

In [6]:
from re import search,sub,compile

Функция search(r,s) ищет подстроку, соответствующую регулярному выражению r, в строке s. Она возвращает объект сопоставления или None, если такая подстрока не найдена. Из объекта сопоставления можно добыть полезную информацию о том, как регулярное выражение сопоставлено подстроке.

In [7]:
s='xy xay xaby xby'
m=search('x.y',s)
if m:
    print(m.start(),m.end(),m.group())
else:
    print('Не найдено')
3 6 xay

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

In [8]:
sub('x.y','XXX',s)
Out[8]:
'xy XXX xaby XXX'

Если мы хотим заменить только первые n подстрок, соответствующих образцу, добавляем параметр n.

In [9]:
sub('x.y','XXX',s,1)
Out[9]:
'xy XXX xaby xby'

Если мы хотим использовать одно и то же регулярное выражение несколько раз, можно скомпилировать его в некий объект, и вместо функций модуля re вызывать методы этого объекта. Это повысит эффективность.

In [10]:
xy=compile('x.y')
m=xy.search(s)
print(m.group())
xay
In [11]:
xy.sub('XXX',s)
Out[11]:
'xy XXX xaby XXX'

В регулярном выражении все символы, кроме специальных, обозначают сами себя. Специальные символы - это ., ^, $, *, +, ?, \, |, {, }, [, ], (, ). Если требуется включить в регулярное выражение один из этих символов буквально, как обычный, перед ним нужно поставить \. Вообще, в регулярных выражениях часто приходится использовать \. А чтобы включить этот символ в строку на питоне, его надо писать как \\. Например, регулярное выражение, которое сопоставляется с символом \ - это \\; в виде питонской строки его приходится писать как '\\\\'. Неудобно. Поэтому для записи регулярных выражений часто используют сырые строки (raw, в смысле неприготовленные - не варёные и не жареные). Они пишутся как r'строка' или r"строка", в них \ является вполне обычным символом. Так что это же регулярное выражение можно записать в виде сырой строки r'\\'.

In [12]:
r'\n'
Out[12]:
'\\n'

Специальный символ . в регулярном выражении сопоставляется с любым (одним!) символом в строке (кроме символа '\n'). Мы это уже видели. Конструкция [abc] сопоставляется с a, b или c. В ней можно использовать диапазоны: [0-9] - это любая цифра, а [a-zA-Z] - любая латинская буква. Но тут надо быть осторожным: это диапазоны в юникоднм порядке. Латинские буквы в нём действительно идут подряд, так что для обычных латинских букв (без каких-нибудь умляютов) это правильно. Но вот [а-яА-Я] не включает все расские буквы - ё и Ё находятся вне этих диапазонов. Большинство специальных символов теряют свою специальность между [ и ] и рассматриваются как обычные. Если первым символом после [ идёт ^, то это значит любой символ, кроме тех, что дальше перечислены.

Юникод умеет много гитик, поэтому надёжнее использовать заранее определённые классы символов. Так, \d означает любую цифру (в юникоде, в добавок к [0-9], их есть ещё много); \w означает любой символ, который может присутствовать в слове - букву, цифру или _; \s означает любой "пробел" (пустое пространство, включая табуляцию, '\n' и т.д.). Заглавные буквы означают дополнения к этим множествам: \D - любая не-цифра; \W - не встречается в словах (т.е. не буква, не цифра и не _); \S - любой не-пробел.

После подвыражения в регулярном выражении можно поставить *, это означает любое число повторений (от 0 до $\infty$) этого подвыражения. Например, .* сопоставляется с абсолютно чем угодно. Если поставить +, то это любое ненулевое число повторений (от 1 до $\infty$). А если поставить ?, то это 0 или 1 вхождение, т.е. это предыдущее подвыражение может присутствовать либо отсутствовать. Можно задать возможные числа повторений более явно: а{5} означает 5 букв а, а{2,4} - от 2 до 4 букв а, а{6,} - от 6 до $\infty$ букв а. Но это используется редко.

In [13]:
m=search(r'\d+','abc123xyz')
print(m.group())
123

^ означает начало строки, а $ - конец.

In [14]:
for s in ['0ab','a0b']:
    if search(r'^0',s):
        print('нашли')
    else:
        print('не нашли')
нашли
не нашли

x|y означает x или y. Подвыражения можно заключать в скобки.

In [15]:
m=search(r'(a\d+b)|(c\d+d)','xxxc123dyyy')
print(m.group())
c123d
In [16]:
m=search(r'0(ab)*1','x0ababab1y')
print(m.group())
0ababab1

Но главная польза от скобок не в этом. Каждая скобка создаёт группу. При поиске регулярного выражения в строке куски строки, сопоставленные каждому подвыражению в скобках, запоминаются, и их можно извлечь и использовать. Метод group объекта сопоставления, вызванный без параметров, возвращает подстроку, сопоставленную всему регулярному выражению в целом; если же его вызвать с целым параметром n, то он возвращает n-ую группу.

In [17]:
m=search(r'(^[a-z]*)(\d*)([a-z]*$)','abc123xyz')
print(f'1: {m.group(1)}, 2: {m.group(2)}, 3: {m.group(3)}')
1: abc, 2: 123, 3: xyz

Ещё более это полезно в вызовах sub. Почти всегда строка-замена должна содержать в себе части исходной строки, найденные при сопоставлении с регулярным выражением. Для этого в строке-замене можно использовать обозначения \1, \2 и т.д. - они означают 1-ю, 2-ю и т.д. группы.

In [18]:
sub(r'(^[a-z]*)(\d*)([a-z]*$)',r'[\1][\2][\3]','abc123xyz')
Out[18]:
'[abc][123][xyz]'