Существует два основных способа реализации сопоставлений: с помощью функций и с помощью методов предварительно скомпилированных объектов шаблонов. Второй способ, основанный на предварительно скомпилированных шаблонах, обеспечивает более высокую скорость выполнения, когда один и тот же шаблон применяется многократно — ко всем строкам в текстовом файле, например.
Для демонстрации попробуем выполнить поиск в следующих строках (полный листинг интерактивного сеанса, который приводится в этом разделе, можно найти в файле re-interactive.txt):
> >> text1 = ‘Hello spam…World’
> >> text2 = ‘Hello spam…other’
При сопоставлении, выполняемом следующим программным кодом, не производится предварительная компиляция шаблона: сопоставление выполняется немедленно и отыскивает все символы между словами «Hello» и «World» в текстовых строках:
> >> import re
> >> matchobj = re.match(‘Hello(.*)World’, text2)
> >> print(matchobj)
None
Когда сопоставление завершается неудачей, как в данном примере (строка text2 не содержит слово «World»), обратно возвращается объект None, который при проверке инструкцией if интерпретируется как «ложь».
В строке шаблона, использованной здесь в качестве первого аргумента функции re.match, слова «Hello» и «World» соответствуют сами себе, а конструкция (.*) означает — любой символ (.), повторяющийся ноль или более раз (*). Наличие круглых скобок сообщает интерпретатору, что он должен сохранить часть строки, совпавшую с этой конструкцией, в виде группы — подстроки, доступной после операции сопоставления. Чтобы увидеть, как это происходит, рассмотрим пример, где сопоставление завершается успехом:
> >> matchobj = re.match(‘Hello(.*)World’, text1)
> >> print(matchobj)
<_sre.SRE_Match object at 0x009D6520>
> >> matchobj.group(1)
‘ spam…’
Когда сопоставление завершается успехом, обратно возвращается объект со от вет ст вия, который предоставляет интерфейс для извлечения совпавших подстрок — вызов group(1) возвращает первый, самый левый фрагмент испытуемой строки, совпавший с частью шаблона, заключенной в круглые скобки (с нашей конструкцией (.*)). Как уже упоминалось, операция сопоставления не просто дает ответ «да/нет» — заключая части шаблонов в круглые скобки, можно также получать совпавшие подстроки. В данном случае мы получили текст, находящийся между словами «Hello» и «World». Группу с номером 0 — всю строку, совпавшую с полным шаблоном, — удобно использовать, когда необходимо убедиться, что шаблон охватил весь текст испытуемой строки.
> >> pattobj = re.compile(‘Hello(.*)World’)
> >> matchobj = pattobj.match(text1)
> >> matchobj.group(1) ‘ spam…’
Напомню, что предварительная компиляция необходима для повышения скорости работы, когда один и тот же шаблон используется многократно, что обычно происходит при построчном сканировании содержимого файлов. Ниже демонстрируется немного более сложный пример, иллюстрирующий обобщенность шаблонов. Этот шаблон допускает наличие нуля и более символов пробела или табуляции в начале строки ([ \t]*), пропускает один или более таких символов после слова «Hello» ([ \t]+), сохраняет символы в середине ((.*)) и допускает, что завершающее слово может начинаться с заглавной или строчной буквы ([Ww]). Как видите, шаблоны способны обрабатывать самые разнообразные данные:
> >> patt = ‘[ \t]*Hello[ \t]+(.*)[Ww]orld’
> >> line = ‘ Hello spamworld’
> >> mobj = re.match(patt, line)
> >> mobj.group(1)
‘spam’
Обратите внимание, что в последнем фрагменте мы сопоставляли шаблон типа str со строкой типа str. Мы можем также сопоставлять строки типа bytes, чтобы обработать, например, кодированный текст, но мы не можем смешивать строки разных типов (ограничение, которое действует в Python повсюду, — интерпретатор не имеет информации о кодировке, необходимой для преобразования строк байтов в текст Юникода):
> >> patt = b‘[ \t]*Hello[ \t]+(.*)[Ww]orld‘ # строки bytes допустимы
> >> line = b‘ Hello spamworld‘ # возвращает группу типа bytes
> >> re.match(patt, line).group(1) # но нельзя смешивать str/bytes
b’spam’
> >> re.match(patt, ‘ Hello spamworld’)
TypeError: can’t use a bytes pattern on a string-like object
(TypeError: нельзя смешивать шаблоны типа bytes со строковыми объектами)
>>> re.match(‘[ \t]*Hello[ \t]+(.*)[Ww]orld’, line)
TypeError: can’t use a string pattern on a bytes-like object (TypeError: нельзя смешивать строковые шаблоны с объектами типа bytes)
В дополнение к инструментам, продемонстрированным в этих примерах, существуют инструменты для сканирования вперед, в поисках одного совпадения (search), всех совпадений (findall), разбиения и замены по шаблону и так далее. Все они имеют аналогичные реализации в виде функций модуля и методов скомпилированных шаблонов. В следующем разделе приводятся несколько примеров, демонстрирующих основные возможности.
Использованная литература:
Марк Лутц — Программирование на Python, 4-е издание, II том, 2011