Шаблон – это описание того, как выглядит подходящий кусок текста.
| Кусок шаблона |
Что означает |
[0-9] |
любая цифра |
[1-9] |
любая цифра, кроме нуля |
[-*] |
минус или звёздочка (внутри скобок перечисляются символы, и минус первым – это просто минус) |
* |
то, что стоит перед ним, повторяется сколько угодно раз, в том числе ни разу |
+ |
то же, но хотя бы один раз |
(?:…) |
скобки: повторитель относится ко всему, что внутри них, а не к последнему символу |
Плюс вертикальная черта | – «или».
Зачем ?: внутри скобок. Обычные скобки ( ) делают два дела сразу: группируют кусок и запоминают то, что в него попало. Запоминание нужно, когда из найденного надо достать часть – день из даты, имя из строки, – и тогда findall возвращает не куски целиком, а запомненное, по одному значению на каждую пару скобок. Нам доставать нечего, нужна длина куска, поэтому запоминание выключают: ?: сразу после открывающей скобки.
Разницу видно в виджете ниже, кнопка «скобки без ?:»: шаблон тот же, подсветка та же, а список под текстом превращается в ('7', '*0', '0') и дальше такие же тройки. В шаблоне три пары скобок – значит, в каждой тройке три элемента, и max(map(len, …)) честно считает длину тройки: 3 вместо 154. Программа не падает, просто печатает не то, поэтому ошибку легко не заметить.
Этого набора хватает, чтобы перевести определение из шага 1 слово в слово.
Число – одиночный 0 или цифра от 1 до 9 с любым хвостом из цифр:
(?:0|[1-9][0-9]*)
Скобки здесь обязательны: без них | разделило бы шаблон целиком, а не два варианта числа.
Знак и следующее за ним число – это то же число, перед которым стоит - или *:
(?:[-*](?:0|[1-9][0-9]*))
Выражение – число, за которым такая пара повторяется сколько угодно раз. «Сколько угодно раз» – это * после скобок:
(?:0|[1-9][0-9]*)(?:[-*](?:0|[1-9][0-9]*))*
Оба ограничения условия шаблон соблюдает сам: два знака подряд не пройдут, потому что после знака обязано идти число, а ведущий ноль – потому что число начинается либо с 0 и на этом кончается, либо с цифры от 1 до 9.
re.findall возвращает список всех кусков, подошедших под шаблон, а дальше нужен самый длинный:
import re
with open('demo_24.txt') as f:
s = f.read().strip() # strip() убирает перевод строки в конце файла
# число: одиночный 0 или цифра 1-9 с любым хвостом – ведущие нули не пройдут
# выражение: число, за ним сколько угодно пар «знак + число»
pattern = r'(?:0|[1-9][0-9]*)(?:[-*](?:0|[1-9][0-9]*))*'
print(max(map(len, re.findall(pattern, s)))) # 154
Остальные возможности шаблонов – sub, жадность, границы слова, просмотр вперёд – в учебнике: язык шаблонов и регулярные выражения на практике.