В этом руководстве в Python, мы ознакомимся с регулярными выражениями и модулем re
. Регулярные выражения (regex) - это механизм поиска и замены текста. Данный формальный язык встроен во многие языки программирования, и являются стандартным инструментом для большинства проектов.
Областью применения модуля re
является все, что связано с текстовыми операциями: проверка пользовательского ввода, поиск по странице или файлу, разбор текстовой информации и многое другое. Но будьте осторожны, использование этого инструмента требует некоторых усилий.
Для работы с модулем, его необходимо импортировать:
import re
Что такое шаблон регулярного выражения и как его скомпилировать?
В основе Regex лежат шаблоны, синтаксис, который позволяет описывать текст: какой он длины, из каких символов состоит, в каком случае и так далее. На основе этих моделей осуществляется текстовый поиск.
Простой пример. Шаблон, описывающий любой отдельный символ: «.?». Здесь точка обозначает любой символ, а вопросительный знак указывает число — ровно одно повторение.
Вы можете передать шаблон регулярного выражения в переменную, если планируете использовать его несколько раз. Для этого используется метод compile
.
import re
regex = re.compile('.?')
print(regex.findall('notehunter'))
Результат:
>>> ['n', 'o', 't', 'e', 'h', 'u', 'n', 't', 'e', 'r', '']
Как разделить текст с помощью модуля re?
Модуль re имеет два метода разделения:
re.split()
split()
- метод предпочтительнее если вы хотите использовать одно и тоже выражение несколько раз, так как это объект regex, получаемый методомcompile
.
import re
regex = re.compile('\.')
text = 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore ' \
'magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea ' \
'commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat ' \
'nulla pariatur. '
print('regex.split:', regex.split(text))
print('re.split:', re.split('\.', text))
Результат:
>>> re.split: ['Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua', ' Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat', ' Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur', ' ']
>>> regex.split: ['Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua', ' Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat', ' Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur', ' ']
Как видите, два метода отработали совершенно одинаково.
Поиск совпадений с использованием findall search и match
Теперь мы рассмотрим, как найти определенный фрагмент текста с помощью шаблонов regex.
import re
regex = re.compile('В ....')
text = 'В мире горы есть и долины есть, ' \
'В мире хоры есть и низины есть, ' \
'В мире море есть и лавины есть, ' \
'В мире боги есть и богини есть.'
print('regex.findall:', regex.findall(text))
Результат:
>>> regex.findall: ['В мире', 'В мире', 'В мире', 'В мире']
Метод findall
ищет все фрагменты текста, соответствующие указанному шаблону, и возвращает список найденных вхождений. В этом примере мы искали все разделы, начинающиеся со слова «В», за которым следует пробел и любые четыре символа.
re.search и re.match - отличия
Метод match
ищет указанный шаблон только в начале строки. Этот метод возвращает один объект, содержащий начальный и конечный индексы найденной подстроки:
import re
regex = re.compile('В мире')
text = 'В мире горы есть и долины есть, ' \
'В мире хоры есть и низины есть, ' \
'В мире море есть и лавины есть, ' \
'В мире боги есть и богини есть.'
print('regex.match:', regex.match(text))
print('regex.match.start:', regex.match(text).start())
print('regex.match.end:', regex.match(text).end())
Результат:
>>> regex.match: <re.Match object; span=(0, 6), match='В мире'>
>>> regex.match.start: 0
>>> regex.match.end: 6
Чтобы вывести само значение, нужно добавить метод group()
import re
regex = re.compile('В мире')
text = 'В мире горы есть и долины есть, ' \
'В мире хоры есть и низины есть, ' \
'В мире море есть и лавины есть, ' \
'В мире боги есть и богини есть.'
print('regex.match.group:', regex.match(text).group())
Результат:
>>> regex.match.group: В мире
Метод search
похож на метод match
, но он может искать подстроки в любом месте текста. Возвращает тот же объект, что и match
.
import re
regex = re.compile('горы')
text = 'В мире горы есть и долины есть, ' \
'В мире хоры есть и низины есть, ' \
'В мире море есть и лавины есть, ' \
'В мире боги есть и богини есть.'
print('regex.search:', regex.search(text))
print('regex.search.start:', regex.search(text).start())
print('regex.search.end:', regex.search(text).end())
print('regex.search.group:', regex.search(text).group())
Результат:
>>> regex.search: <re.Match object; span=(7, 11), match='горы'>
>>> regex.search.start: 7
>>> regex.search.end: 11
>>> regex.search.group: горы
Замена фрагмента текста на другой с помощью regex
Чтобы заменить один фрагмент текста другим, используйте метод sub
. В этом примере существует два способа реализации:
import re
regex = re.compile('В мире')
text = 'В мире горы есть и долины есть, ' \
'В мире хоры есть и низины есть, ' \
'В мире море есть и лавины есть, ' \
'В мире боги есть и богини есть.'
print('regex.sub:', regex.sub('На земле', text))
print('regex.sub:', re.sub('В мире', 'На земле', text))
Результат:
>>> regex.sub: На земле горы есть и долины есть, На земле хоры есть и низины есть, На земле море есть и лавины есть, На земле боги есть и богини есть.
>>> re.sub: На земле горы есть и долины есть, На земле хоры есть и низины есть, На земле море есть и лавины есть, На земле боги есть и богини есть.
Группы регулярных выражений
Группы регулярных выражений — это функция, позволяющая извлекать нужные объекты соответствия как отдельные элементы.
Для группировки шаблонов регулярного выражения используются круглые скобки (...), довольно часто такая практика является полезной.
import re
regex = re.compile(r'\w{2,9}\sis')
text = 'Beautiful is better than ugly. ' \
'Explicit is better than implicit. ' \
'Simple is better than complex. ' \
'Complex is better than complicated. ' \
'Flat is better than nested. ' \
'Sparse is better than dense. '
print('regex.findall_non_group:', regex.findall(text))
regex = re.compile(r'(\w{4,9})\sis')
print('regex.findall_group:', regex.findall(text))
Результат:
>>> regex.findall_non_group: ['Beautiful is', 'Explicit is', 'Simple is', 'Complex is', 'Flat is', 'Sparse is']
>>> regex.findall_group: ['Beautiful', 'Explicit', 'Simple', 'Complex', 'Flat', 'Sparse']
Жадное и ленивое соответствие в regex
Регулярные выражения могут быть жадными (greedy) или ленивыми (lazy). Если они жадные, а именно такое поведение по умолчанию, то они попытаются найти как можно больше текста, соответствующего шаблону.
Рассмотрим пример:
import re
regex = re.compile(r'.*\.')
text = 'Beautiful is better than ugly. ' \
'Explicit is better than implicit. ' \
'Simple is better than complex. ' \
'Complex is better than complicated. ' \
'Flat is better than nested. ' \
'Sparse is better than dense. '
print('regex.findall_greedy:', regex.findall(text))
regex = re.compile(r'.*?\.')
print('regex.findall_lazy:', regex.findall(text))
Результат:
>>> regex.findall_greedy: ['Beautiful is better than ugly. Explicit is better than implicit. Simple is better than complex. Complex is better than complicated. Flat is better than nested. Sparse is better than dense.']
>>> regex.findall_lazy: ['Beautiful is better than ugly.', ' Explicit is better than implicit.', ' Simple is better than complex.', ' Complex is better than complicated.', ' Flat is better than nested.', ' Sparse is better than dense.']
В результате видно, что в первом случае Regex показало совпадение до последней точки, однако точка встречается и в конце первой строки, это и есть жадный поиск. И если добавить в конце шаблона Regex вопросительный знак, то мы получим ленивое поведение, которое позволит модулю re
останавливаться на каждом найденном фрагменте.
Наиболее распространенный синтаксис и шаблоны регулярных выражений
Ниже приведен список наиболее часто используемых шаблонов.
- Специальные символы,
- ‘.’ — любой символ;
- ‘^’ — началу строки;
- ‘$’ — конец строки;
- ‘*’ — 0 или более повторений;
- ‘+’ — 1 или более повторений;
- ‘?’ — 0 или 1 повторений;
- ‘*?’, ‘+?’,’??’ — ограничение жадности;
- ‘{m}’ — m повторений;
- ‘{m,n}’ — как можно больше повторений в промежутке от m до n ;
- ‘{m,n}?’ — как можно меньше повторений в промежутке от m до n;
- ‘\’ — экранирование специальных символов;
- ‘[]’ — символьный класс;
- ‘|’ — или;
- ‘(…)’ — группа с захватом;
- Расширения регулярных выражений,
- ‘(?aiLmsux)’ — установка флагов регулярного выражения;
- ‘(?aiLmsux-imsx:…)’ — установка и удаление флагов;
- ‘(?:…)’ — группа без захвата;
- ‘(?P<name>…)’ — именованная группа;
- ‘(?P=name)’ — обратная ссылка на именованную группу;
- ‘(?#…)’ — комментарий;
- ‘(?=…)’ — опережающая позитивная проверка;
- ‘(?!…)’ — опережающая негативная проверка;
- ‘(?<=…)’ — позитивная ретроспективная проверка;
- ‘(?<!…)’ — негативная ретроспективная проверка;
- ‘(?(id/name)yes-pattern|no-pattern)’ — стараться соответствовать yes-pattern;
- Специальные последовательности.
- ‘\number’ — соответствие группы с тем же номером;
- ‘\A’ — только начало строки;
- ‘\b’ — пустая строка (начало или конец слова);
- ‘\B’ — пустая строка (НЕ начало или конец слова);
- ‘\d’ — любая десятичная цифра;
- ‘\D’ — НЕ десятичная цифра;
- ‘\s’ — пробельный символ;
- ‘\S’ — НЕ пробельный символ;
- ‘\w’ — символы, которые могут быть частью слова;
- ‘\W’ — символы, которые НЕ могут быть частью слова;
- ‘\Z’ — только конец строки;
Практические примеры регулярных выражений
Любой символ кроме новой строки:
import re
regex = re.compile(r'.')
text = 'Beautiful is better than ugly.'
print('regex.findall:', regex.findall(text))
Результат:
>>> regex.findall: ['B', 'e', 'a', 'u', 't', 'i', 'f', 'u', 'l', ' ', 'i', 's', ' ', 'b', 'e', 't', 't', 'e', 'r', ' ', 't', 'h', 'a', 'n', ' ', 'u', 'g', 'l', 'y', '.']
Всё, кроме точек и пробелов:
import re
regex = re.compile(r'[^\s,^\.]')
text = 'Beautiful is better than ugly.'
print('regex.findall:', regex.findall(text))
Результат:
>>> regex.findall: ['B', 'e', 'a', 'u', 't', 'i', 'f', 'u', 'l', 'i', 's', 'b', 'e', 't', 't', 'e', 'r', 't', 'h', 'a', 'n', 'u', 'g', 'l', 'y']
Любые цифры:
import re
regex = re.compile(r'\d+')
text = 'Date: 31.10.2022'
print('regex.findall:', regex.findall(text))
Результат:
>>> regex.findall: ['31', '10', '2022']
Все кроме цифр:
import re
regex = re.compile(r'\D+')
text = 'Date: 31.10.2022'
print('regex.findall:', regex.findall(text))
Результат:
>>> regex.findall: ['Date: ', '.', '.']
Любая буква, или цифра:
import re
regex = re.compile(r'\w+')
text = 'Date: 31.10.2022'
print('regex.findall:', regex.findall(text))
Результат:
>>> regex.findall: ['Date', '31', '10', '2022']
Все кроме букв и цифр:
import re
regex = re.compile(r'\W+')
text = 'Date: 31.10.2022'
print('regex.findall:', regex.findall(text))
Результат:
>>> regex.findall: [': ', '.', '.']
Только буквы:
import re
regex = re.compile(r'[A-Za-z]+')
text = 'Date: 31.10.2022'
print('regex.findall:', regex.findall(text))
Результат:
>>> regex.findall: ['Date']
Соответствие заданному количеству раз, в примере 4 буквы, либо от 5 до 7 букв:
import re
regex = re.compile(r'\w{4}')
text = 'Beautiful is better than ugly.'
print('regex.findall_4:', regex.findall(text))
regex = re.compile(r'\w{5,7}')
print('regex.findall_5_7:', regex.findall(text))
Результат:
>>> regex.findall_4: ['Beau', 'tifu', 'bett', 'than', 'ugly']
>>> regex.findall_5_7: ['Beautif', 'better']
Одно и более вхождение:
import re
regex = re.compile(r'\w+a+\w+')
text = 'Beautiful is better than ugly.'
print('regex.findall:', regex.findall(text))
Результат:
>>> regex.findall: ['Beautiful', 'than']
Любое количество вхождений (0 или более раз):
import re
regex = re.compile(r'a+u*\w+')
text = 'Beautiful is better than ugly.'
print('regex.findall:', regex.findall(text))
Результат:
>>> regex.findall: ['autiful', 'an']
Ноль или одно вхождение:
import re
regex = re.compile(r'u+t?\w+')
text = 'Beautiful is better than ugly.'
print('regex.findall:', regex.findall(text))
Результат:
>>> regex.findall: ['utiful', 'ugly']
Граница слова
Для определения границы слова, то есть там, где в тексте есть соседний символ, который может состоять из слова (\w)
и пробела (\s)
, используйте символ \b
.
import re
regex = re.compile(r'\b\w\w')
text = 'Beautiful is better than ugly.'
print('regex.findall_first_two_letters:', regex.findall(text))
regex = re.compile(r'\w\w\b')
print('regex.findall_last_two_letters:', regex.findall(text))
Результат:
>>> regex.findall_first_two_letters: ['Be', 'is', 'be', 'th', 'ug']
>>> regex.findall_last_two_letters: ['ul', 'is', 'er', 'an', 'ly']
Источником послужил: pythoninfo.ru