Python regex encuentra todas las coincidencias superpuestas?

98

Estoy tratando de encontrar cada serie de números de 10 dígitos dentro de una serie más grande de números usando re en Python 2.6.

Puedo obtener fácilmente coincidencias no superpuestas, pero quiero todas las coincidencias en la serie de números. P.ej.

en "123456789123456789"

Debería obtener la siguiente lista:

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

Encontré referencias a una "búsqueda anticipada", pero los ejemplos que he visto solo muestran pares de números en lugar de agrupaciones más grandes y no he podido convertirlos más allá de los dos dígitos.

danspants
fuente
6
Las soluciones presentadas no funcionarán cuando las coincidencias superpuestas comiencen en el mismo punto, por ejemplo, al hacer coincidir "a | ab | abc" con "abcd" solo se devolverá un resultado. ¿Existe una solución para eso que no implique llamar a match () varias veces, haciendo un seguimiento manual del límite 'final'?
Vítor De Araújo
@ VítorDeAraújo: las expresiones regulares superpuestas como (a|ab|abc)generalmente se pueden reescribir como no superpuestas con grupos de captura anidados, por ejemplo (a(b(c)?)?)?, donde ignoramos todos menos el grupo de captura más externo (es decir, el más a la izquierda) al desempaquetar una coincidencia; es cierto que esto es un poco doloroso y menos legible. Esta también será una expresión regular más eficaz para coincidir.
smci

Respuestas:

175

Utilice un grupo de captura dentro de una anticipación. La búsqueda anticipada captura el texto que le interesa, pero la coincidencia real es técnicamente la subcadena de ancho cero antes de la anticipación, por lo que técnicamente las coincidencias no se superponen:

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]
carne_mecánica
fuente
2
Mi respuesta es al menos 2 veces más rápida que esta. Pero esta solución es complicada, la apoyo.
eyquem
16
Explicación = en lugar de buscar el patrón (10 dígitos), busca cualquier cosa SEGUIDA POR el patrón. Entonces encuentra la posición 0 de la cadena, la posición 1 de la cadena y así sucesivamente. Luego toma el grupo (1) - el patrón coincidente y hace una lista de ellos. Muy genial.
Tal Weiss
No tenía idea de que pudiera usar grupos coincidentes dentro de lookaheads, que normalmente no se supone que estén incluidos en una coincidencia (y los subgrupos coincidentes de hecho no aparecen en la coincidencia completa). Como esta técnica todavía parece funcionar en Python 3.4, supongo que se considera una característica más que un error.
JAB
10
Me uní a StackOverflow, respondí preguntas y levanté mi reputación para poder votar esta respuesta. Estoy atascado con Python 2.4 por ahora, así que no puedo usar las funciones de expresiones regulares más avanzadas de Python 3, y este es el tipo de truco extraño que estaba buscando.
TheSoundDefense
2
¿Podría agregar más explicación al código? No es la mejor manera según Stack Overflow, solo tener código en una respuesta. Definitivamente ayudará a la gente.
Akshay Hazari
77

También puede intentar usar el módulo de tercerosregex (no re), que admite coincidencias superpuestas.

>>> 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
David C
fuente
17

Me gustan las expresiones regulares, pero no son necesarias aquí.

Simplemente

s =  "123456789123456789"

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

resultado

1234567891
2345678912
3456789123
4567891234
5678912345
6789123456
7891234567
8912345678
9123456789
eyquem
fuente
10
Las expresiones regulares solo no son necesarias aquí porque está aplicando el conocimiento especial "dentro de una serie más grande de números", por lo que ya sabe que cada posición 0 <= i < len(s)-n+1está garantizada como el comienzo de una coincidencia de 10 dígitos. También me imagino que su código podría acelerarse, sería interesante codificar golf por velocidad.
smci