2.35. Основы шаблонов¶
Регулярное выражение — это небольшой язык для описания строк по их форме, а не по точному содержимому. Используйте его, когда строка соответствует тогда и только тогда, когда она следует шаблону, который вы можете описать, но не можете перечислить — «последовательность цифр, за которой следует единица измерения», «строка, начинающаяся с ERROR и заканчивающаяся числом», «любое из этих расширений файлов в любом порядке с необязательными префиксами v».
Обращайтесь к re только тогда, когда обычный строковый метод не подходит.
str.startswith(),str.endswith()— проверка фиксированного префикса или суффикса.in— проверка наличия фиксированной подстроки.str.split(),str.find(),str.replace()— работа с фиксированными разделителями.
Каждый из них быстрее, легче читается и менее подвержен ошибкам, чем эквивалентное регулярное выражение. Используйте регулярные выражения, когда важна форма строки, а точная подстрока — нет.
2.35.1. Четыре вещи, которыми вы будете пользоваться¶
Модуль MicroPython re предоставляет четыре вещи:
re.compile()— превратить строку шаблона в скомпилированный объект шаблона, который можно использовать повторно.re.match()— попробовать шаблон в начале строки. Шаблон привязан к позиции 0.re.search()— попробовать шаблон в любом месте строки. Возвращает первое совпадение.re.sub()— найти каждое совпадение и заменить его.
Заметные отличия от CPython: нет re.findall, нет re.finditer, нет re.split на уровне модуля (вместо этого у скомпилированных шаблонов есть метод split), нет re.fullmatch, нет констант флагов вроде re.IGNORECASE. Там, где в CPython вы обратились бы к одной из них, постройте эквивалент из re.search() в цикле.
2.35.2. Первый шаблон¶
Шаблон r'\d+' соответствует одной или более цифрам:
>>> import re
>>> m = re.search(r'\d+', 'sensor reading 42 ok')
>>> m.group(0)
'42'
На что стоит обратить внимание:
Шаблон записан как сырая строка (
r'...'), чтобы обратная косая черта в\dдостиглаre, а не была обработана как escape-последовательность строки Python. Всегда используйте сырые строки для шаблонов регулярных выражений.re.search()возвращает объект совпадения при успехе иNoneпри неудаче. Всегда проверяйте результат перед вызовомmatch.group().m.group(0)— это полный текст, которому соответствовал шаблон. Группы 1, 2, … появляются позже, когда шаблон содержит захватывающие круглые скобки.
Тот же шаблон с re.match() возвращает None, потому что строка не начинается с цифры:
>>> re.match(r'\d+', 'sensor reading 42 ok') is None
True
>>> re.match(r'\d+', '42 readings')
<match num=1>
2.35.3. Составные части шаблона¶
Большинство полезных шаблонов строятся из небольшого набора составных частей. Те, что работают в MicroPython:
Литеральные символы — любой символ, не являющийся специальным, соответствует самому себе. hello соответствует hello.
Специальные символы — . ^ $ * + ? { } [ ] \ | ( ) все имеют значения, описанные ниже. Чтобы сопоставить один из них буквально, экранируйте его обратной косой чертой: \. соответствует литеральной точке.
Классы символов — сокращения для распространённых наборов символов:
\d— любая цифра0-9\D— любой не-цифровой символ\s— любой пробельный символ (пробел, табуляция, перевод строки)\S— любой непробельный символ\w— любой символ «слова»: буквы, цифры, подчёркивание\W— любой символ, не являющийся символом слова.— любой символ, кроме перевода строки
Квантификаторы — сколько раз должна совпасть предыдущая часть:
*— ноль или более (жадный)+— один или более (жадный)?— ноль или один{n}— ровно n{m,n}— от m до n (включительно)
Комбинирование: \d{3}-\d{4} соответствует трём цифрам, дефису, четырём цифрам. \s+ соответствует одному или более пробельным символам. hello.*world соответствует hello, чему угодно (включая ничего), затем world.
Примечание
Жадный означает, что квантификатор поглощает столько входных данных, сколько может, при этом по-прежнему позволяя совпасть остальной части шаблона. Против hello x world y world .* в hello.*world соответствует самой длинной последовательности, которая всё ещё оставляет world в конце — он захватывает x world y, а не более короткое x. То же справедливо для + и диапазонной формы {m,n}: движок берёт самое длинное совпадение, какое может, а затем отступает только если остальная часть шаблона не совпадает.
2.35.4. Подстановка¶
re.sub() находит каждое совпадение и заменяет его строкой. Замена может ссылаться на захваченные группы через \1, \2, … (рассматривается далее вместе с остальным синтаксисом групп). Без групп re.sub представляет собой прямой поиск-и-замену по регулярному выражению:
>>> re.sub(r'\s+', ' ', 'too many spaces')
'too many spaces'
>>> re.sub(r'\d+', 'N', 'log 12, log 345, log 6')
'log N, log N, log N'
Третий аргумент — это строка, над которой выполняется операция; результатом является новая строка с заменённым каждым совпадением.
2.35.5. Разбиение — только на скомпилированном шаблоне¶
На уровне модуля нет re.split. Чтобы разбить по регулярному выражению, сначала скомпилируйте шаблон и вызовите его метод split:
>>> sep = re.compile(r'\s*,\s*')
>>> sep.split('a , b,c , d')
['a', 'b', 'c', 'd']
Необязательный второй аргумент ограничивает число разбиений:
>>> sep.split('a, b, c, d', 2)
['a', 'b', 'c, d']
2.35.6. Компиляция для повторного использования¶
Если один и тот же шаблон выполняется много раз — внутри цикла или в часто вызываемой функции — скомпилируйте его один раз и используйте скомпилированный объект повторно:
digit_run = re.compile(r'\d+')
def first_number(line):
m = digit_run.search(line)
return int(m.group(0)) if m else None
Вызов pattern.match() и pattern.search() на скомпилированном объекте делает то же, что и функции уровня модуля, но избегает затрат на перекомпиляцию при каждом вызове.
2.35.7. Шаблоны, которые ничему не соответствуют¶
Три шаблона в особенности ставят разработчиков в тупик:
.*соответствует пустой строке.re.search(r'.*', s).group(0)возвращает''на любых входных данных.Шаблон с неэкранированным специальным символом является синтаксической ошибкой.
re.compile(r'cost: $5')вызываетValueError, потому что$означает «конец строки». Используйтеr'cost: \$5'.Точка
.не соответствует переводу строки. Чтобы сопоставлять через переводы строк, напишите шаблон, обрабатывающий их явно с помощью[\s\S], или подавайте по одной строке за раз.
С этими составными частями шаблон может соответствовать почти любому фиксированному по форме фрагменту текста. Извлечение структурированных данных обратно из совпадения требует захватывающих групп.