Строковые операции и шаблоны Python

strokovye operacii i shablony python Текст и язык

Обратите внимание, что в предыдущем примере была предусмотрена возможность пропуска необязательных пробельных символов и использования заглавных и строчных букв. Это позволяет выделить основную причину, по которой может оказаться предпочтительнее использовать шаблоны, — они поддерживают более обобщенный способ обработки текста, чем строковые методы. Ниже приводится еще один показательный пример: мы уже видели, что строковые методы способны разбивать строки и выполнять замену подстрок, но они не смогут проделать эту работу, если в качестве разделителей используются различные строки:

>>> ‘aaabbbccc’.split(‘—‘)

[‘aaa’, ‘bbb’, ‘ccc’]

>>> ‘aaabbbccc’.replace(‘—‘, ‘…’) # строковые методы используют aaabbbccc # фиксированные строки >>> aaabbb==ccc‘.split([‘—‘, ‘==’])

TypeError: Can’t convert ‘list’ object to str implicitly

(TypeError: Невозможно неявно преобразовать объект ‘list’ в str)

>>> ‘aaabbb==ccc’.replace([‘—‘, ‘==’], ‘…’)

TypeError: Can’t convert ‘list’ object to str implicitly

(TypeError: Невозможно неявно преобразовать объект ‘list’ в str)

Шаблоны не только справляются с подобными задачами, но и дают возможность напрямую определять альтернативы, благодаря особому синтаксису шаблонов. В следующем примере конструкция —|== соответствует ли бо строке —, ли бо строке ==. Конструкции [-=] соответствует либо символ -, либо = (множество символов). А конструкция (?:) может использоваться для группировки вложенных частей шаблона без создания группы, сохраняющей подстроку (функция split интерпретирует группы особым образом):

>>> import re

>>> re.split(‘—‘, ‘aaabbbccc’)

[‘aaa’, ‘bbb’, ‘ccc’]

>>> re.sub(‘—‘, ‘…’, ‘aaabbbccc’) # случай с единственным

# разделителем

‘aaabbbccc’

>>> re.split(‘—|==’, ‘aaabbb==ccc’) # разбить поили ==

[‘aaa’, ‘bbb’, ‘ccc’]

>>> re.sub(‘—|==’, ‘…’, ‘aaabbb==ccc’) # заменитьили == ‘aaabbbccc’

>>> re.split(‘[-=]’, ‘aaabbb=ccc‘) # односимвольные альтернативы

[‘aaa‘, ‘bbb‘, ‘ccc‘]

>>> re.split(‘(—)|(==)’, aaabbb==ccc‘) # результат разбиения

[‘aaa‘, ‘—‘, None, ‘bbb‘, None, ‘==’, ccc‘] # включает группы

>>> re.split(‘(?:—)|(?:==)’, aaabbb==ccc‘) # часть выражения, не группы [‘aaa‘, ‘bbb‘, ‘ccc‘]

Операция разбиения тоже может извлекать простые подстроки при использовании фиксированных разделителей, но шаблоны способны обрабатывать окружающий контекст подобно скобкам, помечать части как необязательные, пропускать пробельные символы и многое другое. Проверки \s* в следующем примере обозначают ноль или более пробельных символов (задан класс символов); \s+ — означают один или более таких символов; /? соответствует необязательному символу слэша; [a-z] — любому строчному символу (задан диапазон); (.*?) обозначает необходимость сохранения подстроки, состоящей из нуля или более любых символов — но не более, чем необходимо для совпадения с оставшейся частью шаблона (минимально); а метод groups используется для извлечения сразу всех подстрок, совпавших с фрагментами шаблона, заключенными в круглые скобки:

>>> ‘spam/ham/eggs’.split(‘/’) [‘spam’, ‘ham’, ‘eggs’]

>>> re.match(‘(.*)/(.*)/(.*)’, ‘spam/ham/eggs’).groups() (‘spam’, ‘ham’, ‘eggs’)

>>> re.match(‘<(.*)>/<(.*)>/<(.*)>’, ‘<spam>/<ham>/<eggs>’).groups() (‘spam’, ‘ham’, ‘eggs’)

>>> re.match(‘\s*<(.*)>/?<(.*)>/?<(.*)>’, <spam>/<ham><eggs>’).groups() (‘spam’, ‘ham’, ‘eggs’)

>>> ‘Hello pattern world!’.split()

[‘Hello’, ‘pattern’, ‘world!’]

>>> re.match(‘Hello\s*([a-z]*)\s+(.*?)\s*!’, ‘Hellopattern world !’).groups() (‘pattern’, ‘world’)

На практике иногда существует более одного совпадения. Метод findall предоставляет возможность разделывать объекты строк в пух и прах — он отыскивает все совпадения с шаблоном и возвращает все совпавшие подстроки (или список кортежей, при наличии нескольких групп). Метод search похож на него, но он прекращает сопоставление после первого же совпадения — он напоминает метод match с начальным сканированием вперед. В следующем примере строковый метод обнаруживает только одну заданную строку, а шаблоны позволяют отыскать и извлечь текст в скобках, находящийся в любом месте строки, даже при наличии дополнительного текста:

>>> ‘<spam>/<ham>/<eggs>’.find(‘ham‘) # отыскать смещение подстроки 8

>>> re.findall(‘<(.*?)>’, ‘<spam>/<ham>/<eggs>’) # отыскать все

[‘spam’, ‘ham’, ‘eggs’] # совпадения/группы

>>> re.findall(‘<(.*?)>’, ‘<spam> / <ham><eggs>’)

[‘spam’, ‘ham’, ‘eggs’]

>>> re.findall(‘<(.*?)>/?<(.*?)>’, ‘<spam>/<ham> <eggs><cheese>’) [(‘spam’, ‘ham’), (‘eggs’, ‘cheese’)]

>>> re.search(‘<(.*?)>/?<(.*?)>’,

‘todays menu: <spam>/<ham><eggs><s>’).groups()

(‘spam‘, ‘ham‘)

При использовании findall особенно удобно использовать оператор (?s), чтобы обеспечить совпадение . с символами кон ца стро ки в многострочном тексте — без этого . будет совпадать с любыми другими символами кроме символов конца строки. Ниже демонстрируется поиск двух смежных строк в скобках с произвольным текстом между ними, с пропуском и без пропуска символов конца строки:

>>> re.findall(‘<(.*?)>.*<(.*?)>’, ‘<spam> \n <ham>\n<eggs>’) # останов

# на \n

[]

>>> re.findall(‘(?s)<(.*?)>.*<(.*?)>’, ‘<spam> \n <ham>\n<eggs>’) # максим. [(‘spam’, ‘eggs’)]

>>> re.findall(‘(?s)<(.*?)>.*?<(.*?)>’, ‘<spam> \n <ham>\n<eggs>’) # миним.

[(‘spam‘, ‘ham‘)]

Чтобы сделать крупные шаблоны более удобочитаемыми, группам с совпавшими подстроками можно даже присваивать име на, используя синтаксис <?P<name>, и после сопоставления извлекать их по именам, однако эта особенность имеет ограниченное применение для findall. В следующем примере выполняется поиск строк с символами «слов» (\w), разделенных символами /; это не более чем операция разбиения строки, но в результате получаются именованные группы, и обе функции, search и findall, выполняют сканирование вперед:

>>> re.search(‘(?P<part1>\w*)/(?P<part2>\w*)’, ‘…aaa/bbb/ccc]’).groups() (‘aaa’, ‘bbb’)

>>> re.search(‘(?P<part1>\w*)/(?P<part2>\w*)’, ‘…aaa/bbb/ccc]’).groupdict() {‘part1’: ‘aaa’, ‘part2’: ‘bbb’}

>>> re.search(‘(?P<part1>\w*)/(?P<part2>\w*)’, ‘…aaa/bbb/ccc]’).group(2) ‘bbb’

>>> re.search(‘(?P<part1>\w*)/(?P<part2>\w*)’, ‘…aaa/bbb/ccc]’).group(‘part2’)

‘bbb’

>>> re.findall(‘(?P<part1>\w*)/(?P<part2>\w*)’, ‘…aaa/bbb ccc/ddd]’) [(‘aaa’, ‘bbb’), (‘ccc’, ‘ddd’)]

Наконец, несмотря на то, что базовых операций, таких как извлечение среза и разбиение, часто бывает вполне достаточно, шаблоны позволяют получить более гибкое решение. В следующем примере используется конструкция [" ], соответствующая любому символу, отличному от символа, следующего за ", и выполняется экранирование дефиса в наборе альтернатив внутри [] с использованием \- , благодаря чему он не воспринимается как разделитель в определении диапазона символов. Здесь выполняются эквивалентные операции извлечения срезов, разбиения и сопоставления, а также более обобщенная операция сопоставления, решающая задачу, непосильную для других двух способов:

>>> line = ‘aaa bbb ccc’

>  >> line[:3], line[4:7], line[8:11] # срез извлекается

(‘aaa‘, ‘bbb‘, ‘ccc‘) # по фиксированным смещениям

>  >> line.split() # разбиение выполняется по фиксированным

[‘aaa’, ‘bbb’, ‘ccc’] # разделителям

>  >> re.split(‘ +’, line) # обобщенное разбиение по разделителям

[‘aaa’, ‘bbb’, ‘ccc’]

>  >> re.findall(‘[" ]+’, line) # поиск строк, не являющихся

# разделителями

[‘aaa‘, ‘bbb‘, ‘ccc‘]

>>> line = aaabbbccc / ddd.-/e&e*e# обработка обобщенного текста

>>> re.findall(‘[“ .\-/]+’, line)

[‘aaa’, ‘bbb’, ‘ccc’, ‘ddd’, ‘e&e*e’]

Если в прошлом вам не приходилось использовать регулярные выражения, от представленных примеров ваша голова может пойти кругом (или вообще отключиться!). Поэтому, прежде чем перейти к другим примерам, познакомимся с некоторыми подробностями, лежащими в основе модуля re и его шаблонов регулярных выражений.

Использованная литература:

Марк Лутц — Программирование на Python, 4-е издание, II том, 2011

Каталог сайтов Всего.ру
Оцените статью
Секреты программирования
Добавить комментарий