Регулярные выражения и модуль re в Python
avatar
7 | (offline)
❤️‍🔥Notehunter Developer
Добавлено:
Категория: Руководства «Python»
Комментариев: 0

В этом руководстве в 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

Комментарии к статье 0
Комментариев нет
Форма добавления комментария (необходима регистрация)