© Яценко Р.Н., 2019-2021
Учебный центр компьютерных технологий "Кит"
Проще говоря, регулярное выражение используется для поиска паттернов в указанной строке. Паттерном может быть все что угодно
Можно создавать паттерны соответствия электронной почте или мобильному номеру. Можно создать паттерны, которые ищут слова в строке, начинающиеся на “a” и заканчивающиеся на “z”
Проверка соответствия фрагментов текста некоторым критериям. Например, наличие символа обозначения валюты и последующих за ним цифр, проверка адреса электронной почты и т.д.
Поиск подстрок, которые в том числе могут иметь несколько форм. Например, поиск 'pet.png'
, 'pet.jpg'
, 'pet.jpeg'
или 'pet.svg'
, но не обнаруживались подстроки 'carpet.png'
и подобные ей
Замена всего, что совпадает с шаблоном, на указанную строку. Например, поиск подстроки 'устройство передвижения, движимое мускульной силой'
и замена подстрокой 'велосипед'
Разбиение строки по точкам совпадения с шаблоном. Например, разбиение строки по ':'
или '='
import re pattern = r"times" string = "It was the best of times, it was the worst of times." print(len(re.findall(pattern,string))) # 2
Для написания и отладки регулярных выражений удобно использовать онлайн-сервисы, например https://regex101.com, которые в том числе содержат библиотеку готовых выражений и могут генерировать код для различных языков программирования
Флаги не входят непосредственно в регулярное выражение, однако расширяют его функции:
'g'
– глобальный поиск (обрабатываются все совпадения с шаблоном поиска)'i'
– регистр букв не имеет значения (по умолчанию любой поиск регистрозависим)'m'
– многострочный поискФлаг указывается после паттерна, например: '/[0-9]/m'
Одним из наиболее простых случаев является поиск отдельных символов и строк
Специальные символы (символы-джокеры должны экранироваться символом обратного слеша '\'
. К ним относятся:
\ . ~ $ ? + * { } [ ] ( ) |
В пределах регулярных выражений можно также использовать большинство стандартных экранированных последовательностей языка Python, например, '\n'
, '\t'
и др.
Для поиска не конкретной последовательности символов, а некоторого их множества предназначены классы символов
Пример:
'[аеёиоуэюя]'
позволяет найти все гласные буквы в строке'п[ое]л'
даст совпадение для слов 'пел'
и 'пол'
, но не найдет слова 'поел'
или 'пил'
группа_символов
. По умолчанию при сопоставлении учитывается регистр.Пример:
'[^аеёиоуэюя]'
обнаружит все символы в тексте, кроме гласных буквпервый
до последний
Пример:
'[0-9]'
найдет все цифры в тексте.
(точка)'.'
соответствует какому-либо одному знаку, кроме '\n'
. Для поиска точки необходимо использовать экранирование: '\.'
Пример:
'м.л'
найдет в тексте 'мел'
, 'мул'
и т.д.\w
Пример:
'\w1'
найдет в тексте '11'
или 'я1'
\W
\s
\S
\d
Пример:
'\d-й'
найдет в тексте '1-й'
или '2-й'
\D
Зачастую требуется не только найти слово по символам или группе символов, но и указать количество возможных повторений, для чего в регулярных выражениях используются квантификаторы
Квантификаторы записываются после символа/строки/множества
Квантификатор | Описание |
---|---|
'*' |
Ноль или более совпадений |
'+' |
Одно или больше совпадений |
'?' |
Ноль или одно совпадение |
'{m}' |
Совпадение ровно m раз |
'{m,}' |
Совпадение m и более раз |
'{m, n}' |
Совпадение от m до n раз |
Каждый квантификатор может быть:
По умолчанию, все квантификаторы являются жадными; для включения «ленивого» режима необходимо поставить знак '?'
после квантификатора
https://regex101.com/r/aGn8QC/2
\b
— граница слова)\b
— граница слова)*
(.
— это точно не конец строки!)В коде регулярные выражения обычно записываются в виде «сырых» строк (с префиксом r
, подавляющих экранирование).
Так, при поиске переноса строки удобнее указать r"\n"
вместо "\\n"
, избежав необходимости дублировать экранирующий символ
re
. При этом во время вызова функции, произойдет компиляция регулярного выражения и дальнейший поиск соответствийre.regex
), а затем использовать его методыФункция | Описание |
---|---|
re.search(pattern, string) |
Найти в строке string первую строчку, подходящую под шаблон pattern |
re.fullmatch(pattern, string) |
Проверить, подходит ли строка string под шаблон pattern |
Функция | Описание |
---|---|
re.split(pattern, string, maxsplit=0) |
Аналог str.split() , только разделение происходит по подстрокам, подходящим под шаблон pattern |
re.findall(pattern, string) |
Найти в строке string все непересекающиеся шаблоны pattern |
Функция | Описание |
---|---|
re.finditer(pattern, string) |
Итератор всем непересекающимся шаблонам pattern в строке string (выдаются match -объекты) |
re.sub(pattern, repl, string, count=0) |
Заменить в строке string все непересекающиеся шаблоны pattern на repl |
import re match = re.search(r'\d\d\D\d\d', r'Телефон 123-12-12') print(match[0] if match else 'Not found') # 23-12 match = re.search(r'\d\d\D\d\d', r'Телефон 1231212') print(match[0] if match else 'Not found') # Not found
match = re.fullmatch(r'\d\d\D\d\d', r'12-12') print('YES' if match else 'NO') # YES match = re.fullmatch(r'\d\d\D\d\d', r'Т. 12-12') print('YES' if match else 'NO') # NO print(re.split(r'\W+', 'Где, скажите мне, мои очки??!')) # ['Где', 'скажите', 'мне', 'мои', 'очки', '']
print(re.findall(r'\d\d\.\d\d\.\d{4}', r'Эта строка написана 19.01.2018, а могла бы и 01.09.2017')) # ['19.01.2018', '01.09.2017'] for m in re.finditer(r'\d\d\.\d\d\.\d{4}', r'Эта строка написана 19.01.2018, а могла бы и 01.09.2017'): print('Дата', m[0], 'начинается с позиции', m.start()) # Дата 19.01.2018 начинается с позиции 20 # Дата 01.09.2017 начинается с позиции 45
print(re.sub(r'\d\d\.\d\d\.\d{4}', r'DD.MM.YYYY', r'Эта строка написана 19.01.2018, а могла бы и 01.09.2017')) # Эта строка написана DD.MM.YYYY, а могла бы и DD.MM.YYYY
Константа | Назначение |
---|---|
re.ASCII |
По умолчанию \w , \W , \b , \B , \d , \D , \s , \S соответствуют все юникодные символы с соответствующим качеством.re.ASCII ускоряет работу, если все соответствия лежат внутри ASCII |
Константа | Назначение |
---|---|
re.IGNORECASE |
Не различать заглавные и маленькие буквы. Работает медленнее, но иногда удобно |
re.MULTILINE |
Специальные символы ^ и $ соответствуют началу и концу каждой строки |
re.DOTALL |
По умолчанию символ \n конца строки не подходит под точку. С этим флагом точка — вообще любой символ |
print(re.findall(r'\d+', '12 + ٦٧')) # ['12', '٦٧'] print(re.findall(r'\w+', 'Hello, мир!')) # ['Hello', 'мир'] print(re.findall(r'\d+', '12 + ٦٧', flags=re.ASCII)) # ['12'] print(re.findall(r'\w+', 'Hello, мир!', flags=re.ASCII)) # ['Hello'] print(re.findall(r'[уеыаоэяию]+', 'ОООО ааааа ррррр ЫЫЫЫ яяяя')) # ['ааааа', 'яяяя'] print(re.findall(r'[уеыаоэяию]+', 'ОООО ааааа ррррр ЫЫЫЫ яяяя', flags=re.IGNORECASE)) # ['ОООО', 'ааааа', 'ЫЫЫЫ', 'яяяя']
text = r""" Торт с вишней1 вишней2 """ print(re.findall(r'Торт.с', text)) # [] print(re.findall(r'Торт.с', text, flags=re.DOTALL)) # ['Торт\nс'] print(re.findall(r'виш\w+', text, flags=re.MULTILINE)) # ['вишней1', 'вишней2'] print(re.findall(r'^виш\w+', text, flags=re.MULTILINE)) # ['вишней2']
re.compile(pattern, flags=0)
pattern
, используя флаги flags
(например, re.MULTILINE
) и возвращает объект класса re.regex
Пример:
import re regex = re.compile("^.+?(\d)") print(regex.search("Привет1 Привет12")[0])
Если функции re.search
, re.fullmatch
не находят соответствие шаблону в строке, то они возвращают None
, функция re.finditer
не выдаёт ничего
Если соответствие найдено, то возвращается match
-объект. Эта штука содержит в себе кучу полезной информации о соответствии шаблону
Метод | Описание |
---|---|
match[0] , match.group() |
Подстрока, соответствующая шаблону |
match = re.search(r'\w+', r'$$ What??') print(match[0]) # 'What'
Метод | Описание |
---|---|
match.start() |
Индекс в исходной строке, начиная с которого идёт найденная подстрока |
match.end() |
Индекс в исходной строке, который следует сразу за найденной подстрокой |
match = re.search(r'\w+', r'$$ What??') print(match.start()) # 3 print(match.end()) # 7
(...)
без ?:
, то они становятся группирующимиre.search
, re.fullmatch
и re.finditer
, по каждой такой группе можно получить ту же информацию, что и по всему шаблону:
(...)
import re pattern = r'\s*([А-Яа-яЁё]+)(\d+)\s*' string = r'--- Опять45 ---' match = re.search(pattern, string) print(f'Найдена строка >{match[0]}< с позиции {match.start(0)}', f'до {match.end(0)}') print(f'Группа букв >{match[1]}< с позиции {match.start(1)}', f'до {match.end(1)}') print(f'Группа цифр >{match[2]}< с позиции {match.start(2)}', f'до {match.end(2)}')
Найдена строка > Опять45 < с позиции 3 до 16
Группа букв >Опять< с позиции 6 до 11
Группа цифр >45< с позиции 11 до 13
match
-объекте будет создана только для последнего соответствия'--- Опять45 ---'
'\s*([А-Яа-яЁё])+(\d)+\s*'
, то вывод был бы таким:Найдена подстрока > Опять45 < с позиции 3 до 16
Группа букв >ь< с позиции 10 до 11
Группа цифр >5< с позиции 12 до 13
Внутри группирующих скобок могут быть и другие группирующие скобки.
В этом случае их нумерация производится в соответствии с номером появления открывающей скобки в шаблоне
import re pattern = r'((\d)(\d))((\d)(\d))' string = r'123456789' match = re.search(pattern, string) print(f'Найдена строка >{match[0]}< с позиции {match.start(0)}', f'до {match.end(0)}') for i in range(1, 7): print(f'Группа №{i} >{match[i]}< с позиции {match.start(i)}', f'до {match.end(i)}')
Найдена строка >1234< с позиции 0 до 4
Группа №1 >12< с позиции 0 до 2
Группа №2 >1< с позиции 0 до 1
Группа №3 >2< с позиции 1 до 2
Группа №4 >34< с позиции 2 до 4
Группа №5 >3< с позиции 2 до 3
Группа №6 >4< с позиции 3 до 4
re.sub
) очень удобную возможность: в шаблоне для замены можно ссылаться на соответствующую группу при помощи
\1, \2, \3, ...
ММ/ДД/ГГГГ
перевести в удобный ДД.ММ.ГГГГ
, то можно использовать такую регулярку:import re text = """ We arrive on 03/25/2018. So you are welcome after 04/01/2018. """ print(re.sub(r'(\d\d)/(\d\d)/(\d{4})', r'\2.\1.\3', text))
We arrive on 25.03.2018.
So you are welcome after 01.04.2018.
(?:часть_выражения)
Пример:
'(?:\w+)\s'
выполнит в тексте поиск слов, отделенных пробельным символом; при этом слово НЕ будет захвачено в отдельную группуОперация выбора позволяет захватить одно из нескольких выражений в качестве результата поиска
выражение1|выражение2|выражение3
'|'
'красн(?:ый|оватый|енький)'
найдет в тексте слова 'красный'
, 'красноватый'
, 'красненький'
; при этом окончание не будет захвачено в группуШаблон | Описание | Пример | Результат |
---|---|---|---|
^ |
Начало всего текста или начало строчки текста, если flag=re.MULTILINE |
^Привет |
|
$ |
Конец всего текста или конец строчки текста, если flag=re.MULTILINE |
Будь здоров!$ |
Шаблон | Описание | Пример | Результат |
---|---|---|---|
\A |
Строго начало всего текста | ||
\Z |
Строго конец всего текста | ||
\b |
Начало или конец слова (слева пусто или не-буква, справа буква и наоборот) | \bвал |
вал, перевал, Перевалка |
Шаблон | Описание | Пример | Результат |
---|---|---|---|
\B |
Не граница слова: либо и слева, и справа буквы, либо и слева, и справа НЕ буквы | \Bвал |
перевал, вал, Перевалка |
\Bвал\B |
перевал, вал, Перевалка |
Шаблон | Описание | Пример | Результат |
---|---|---|---|
(?=...) |
lookahead assertion, соответствует каждой позиции, сразу после которой начинается соответствие шаблону ... |
Isaac (?=Asimov) |
Isaac Asimov, Isaac other |
Шаблон | Описание | Пример | Результат |
---|---|---|---|
(?!...) |
negative lookahead assertion, соответствует каждой позиции, сразу после которой НЕ может начинаться шаблон ... |
Isaac (?!Asimov) |
Isaac Asimov, Isaac other |
Шаблон | Описание | Пример | Результат |
---|---|---|---|
(?<=...) |
positive lookbehind assertion, соответствует каждой позиции, которой может заканчиваться шаблон ... Длина шаблона должна быть фиксированной |
(?<=abc)def |
abcdef, bcdef |
Шаблон | Описание | Пример | Результат |
---|---|---|---|
(?<!...) |
negative lookbehind assertion, соответствует каждой позиции, которой НЕ может заканчиваться шаблон ... |
(?<!abc)def |
abcdef, bcdef |
Шаблон | Комментарий | Результат |
---|---|---|
(?<!\d)\d(?!\d) |
Цифра, окружённая не-цифрами | Text ABC 123 A1B2C3! |
(?<=#START#).*?(?=#END#) |
Текст от #START# до #END# |
text from #START# till #END# |
Шаблон | Комментарий | Результат |
---|---|---|
\d+(?=_(?!_)) |
Цифра, после которой идёт ровно одно подчёркивание | 12_34__56 |
^(?:(?!boo).)*?$ |
Строка, в которой нет boo (то есть нет такого символа, перед которым есть boo) | a foo and boo and zoo>br>and others |
Шаблон | Комментарий | Результат |
---|---|---|
^(?:(?!boo)(?!foo).)*?$ |
Строка, в которой нет ни boo, ни foo | a foo and boo and zoo and others |