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, а не була оброблена як рядкова екрануюча послідовність 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]або обробляйте по одному рядку за раз.
З цими елементами шаблон може збігатися з майже будь-яким фіксованим фрагментом тексту. Для вилучення структурованих даних із збігу потрібні захоплені групи.