Регулярний вираз Python знаходить усі збіги?


98

Я намагаюся знайти кожну 10-значну серію чисел у більшій серії чисел, використовуючи re в Python 2.6.

Я легко можу взяти не збігаються збіги, але я хочу, щоб кожен збіг з числової серії. Напр.

у "123456789123456789"

Я повинен отримати наступний список:

[1234567891,2345678912,3456789123,4567891234,5678912345,6789123456,7891234567,8912345678,9123456789]

Я знайшов посилання на "lookahead", але приклади, які я бачив, показують лише пари чисел, а не більші групування, і я не зміг перетворити їх після двох цифр.


6
Представлені рішення не працюватимуть, коли збіги, що перекриваються, починаються з тієї ж точки, наприклад, зіставлення "a | ab | abc" проти "abcd" поверне лише один результат. Чи існує рішення для цього, яке не передбачає виклику match () кілька разів, відстеження вручну межі "кінця" вручну?
Vítor De Araújo

@ VítorDeAraújo: перекриваються регулярні вирази типу, як (a|ab|abc)правило, можуть бути переписані як такі, що не перекриваються, із вкладеними групами захоплення, наприклад (a(b(c)?)?)?, де ми ігноруємо всі, крім самої зовнішньої (тобто крайньої лівої) групи захоплення, коли розпаковуємо збіг; правда, це трохи боляче і менш розбірливо. Це також буде більш ефективним регулярним виразом.
smci

Відповіді:


175

Використовуйте групу захоплення всередині динаміка. Lookahead фіксує текст, який вас цікавить, але фактично збіг технічно є підрядком нульової ширини перед lookahead, тому технічні збіги технічно не перекриваються:

import re 
s = "123456789123456789"
matches = re.finditer(r'(?=(\d{10}))',s)
results = [int(match.group(1)) for match in matches]
# results: 
# [1234567891,
#  2345678912,
#  3456789123,
#  4567891234,
#  5678912345,
#  6789123456,
#  7891234567,
#  8912345678,
#  9123456789]

2
Моя відповідь принаймні в 2 рази швидша за цю. Але це рішення складне, я підтримую його.
eyquem

16
Пояснення = замість того, щоб шукати шаблон (10 цифр), він шукає будь-що, ЩО ВИКОНАЄТЬСЯ за шаблоном. Тож він знаходить позицію 0 рядка, позицію 1 рядка тощо. Потім він захоплює групу (1) - відповідний шаблон і складає їх список. Дуже круто.
Tal Weiss

Я не уявляв, що ви можете використовувати групи збігу всередині пошукових головок, які зазвичай не повинні бути включені в збіг (а відповідні підгрупи справді не відображаються повними збігами). Оскільки цей прийом все ще працює в Python 3.4, я думаю, він вважається функцією, а не помилкою.
JAB

10
Я приєднався до StackOverflow, відповідав на запитання та підвищував свою репутацію, щоб я міг підтримати цю відповідь. На даний момент я застряг у Python 2.4, тому я не можу використовувати більш розширені функції регулярного виразу Python 3, і це просто такий химерний підступ, який я шукав.
TheSoundDefense

2
Не могли б ви додати більше пояснень до коду. Це не найкращий спосіб відповідно до переповнення стека, щоб просто мати код у відповіді. Це точно допоможе людям.
Акшай Хазарі,

77

Ви також можете спробувати скористатися стороннім regexмодулем (не re), який підтримує накладання збігів.

>>> import regex as re
>>> s = "123456789123456789"
>>> matches = re.findall(r'\d{10}', s, overlapped=True)
>>> for match in matches: print match
...
1234567891
2345678912
3456789123
4567891234
5678912345
6789123456
7891234567
8912345678
9123456789

17

Я люблю регулярні вирази, але вони тут не потрібні.

Просто

s =  "123456789123456789"

n = 10
li = [ s[i:i+n] for i in xrange(len(s)-n+1) ]
print '\n'.join(li)

результат

1234567891
2345678912
3456789123
4567891234
5678912345
6789123456
7891234567
8912345678
9123456789

10
Тут регулярні вирази не потрібні, оскільки ви застосовуєте спеціальні знання "в межах більшої серії чисел", тож ви вже знаєте, що кожна позиція 0 <= i < len(s)-n+1гарантовано буде початком 10-значного збігу. Також я вважаю, що ваш код можна прискорити, було б цікаво кодувати гольф на швидкість.
smci
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.