¿Existe algún mecanismo en Python para combinar expresiones regulares compiladas?
Sé que es posible compilar una nueva expresión extrayendo la propiedad .pattern de cadena antigua simple de los objetos de patrón existentes. Pero esto falla de varias maneras. Por ejemplo:
import re first = re.compile(r"(hello?\s*)") # one-two-three or one/two/three - but not one-two/three or one/two-three second = re.compile(r"one(?P<r1>[-/])two(?P=r1)three", re.IGNORECASE) # Incorrect - back-reference \1 would refer to the wrong capturing group now, # and we get an error "redefinition of group name 'r1' as group 3; was # group 2 at position 47" for the `(?P)` group. # Result is also now case-sensitive, unlike 'second' which is IGNORECASE both = re.compile(first.pattern + second.pattern + second.pattern)El resultado que estoy buscando se puede lograr así en Perl:
$first = qr{(hello?\s*)}; # one-two-three or one/two/three - but not one-two/three or one/two-three $second = qr{one([-/])two\g{-1}three}i; $both = qr{$first$second$second};Una prueba muestra los resultados:
test($second, "...one-two-three..."); # Matches test($both, "...hello one-two-THREEone-two-three..."); # Matches test($both, "...hellone/Two/ThreeONE-TWO-THREE..."); # Matches test($both, "...HELLO one/Two/ThreeONE-TWO-THREE..."); # No match sub test { my ($pat, $str) = @_; print $str =~ $pat ? "Matches\n" : "No match\n"; }¿Hay alguna biblioteca en algún lugar que haga posible este caso de uso en Python? ¿O una función integrada que me falta en alguna parte?
(Nota: una característica muy útil en la expresión regular de Perl anterior es \g{-1} , que se refiere sin ambigüedades al grupo de captura inmediatamente anterior, de modo que no hay colisiones del tipo de las que se queja Python cuando intento compilar el expresión combinada. No he visto eso en ninguna parte del mundo de Python, no estoy seguro de si hay una alternativa en la que no haya pensado).
Ken, este es un problema interesante. Estoy de acuerdo contigo en que la solución de Perl es muy ingeniosa. Se me ocurrió algo, pero no es tan elegante. Tal vez le dé alguna idea para explorar más a fondo la solución usando Python. La idea es simular la concatenación usando métodos Python re.
first = re.compile(r"(hello?\s*)") second = re.compile(r"one(?P<r1>[-/])two(?P=r1)three", re.IGNORECASE) str="...hello one-two-THREEone/two/three..." #str="...hellone/Two/ThreeONE-TWO-THREE..." if re.search(first,str): first_end_pos = re.search(first,str).end() if re.match(second,str[first_end_pos:]): second_end_pos = re.match(second,str[first_end_pos:]).end() + first_end_pos if re.match(second,str[second_end_pos:]): print ('Matches')Funcionará para la mayoría de los casos, pero no funciona para el siguiente caso:
...hellone/Two/ThreeONE-TWO-THREE...Entonces, sí, admito que no es una solución completa a su problema. Espero que esto ayude.
No soy un experto en perl, pero no parece que estés comparando manzanas con manzanas. Está utilizando grupos de captura con nombre en Python, pero no veo ningún grupo de captura con nombre en el ejemplo de Perl. Esto causa el error que mencionas, porque esto
both = re.compile(first.pattern + second.pattern + second.pattern) intenta crear dos grupos de captura llamados r1
Por ejemplo, si usa la expresión regular a continuación, luego intenta acceder a group_one por su nombre, ¿obtendría los números antes de "algún texto" o después?
# Not actually a valid regex r'(?P<group_one>[0-9]*)some text(?P<group_one>[0-9]*)'Solución 1
Una solución fácil es probablemente eliminar los nombres de los grupos de captura. También agregue el re.IGNORECASE a both . El siguiente código funciona, aunque no estoy seguro de que el patrón de expresiones regulares resultante coincida con lo que desea que coincida.
first = re.compile(r"(hello?\s*)") second = re.compile(r"one([-/])two([-/])three", re.IGNORECASE) both = re.compile(first.pattern + second.pattern + second.pattern, re.IGNORECASE)Solución 2
Lo que probablemente haría en su lugar es definir las expresiones regulares separadas como cadenas, luego puedes combinarlas como quieras.
pattern1 = r"(hello?\s*)" pattern2 = r"one([-/])two([-/])three" first = re.compile(pattern1, re.IGNORECASE) second = re.compile(pattern2, re.IGNORECASE) both = re.compile(r"{}{}{}".format(pattern1, pattern2, pattern2), re.IGNORECASE)O mejor aún, para este ejemplo específico, no repita el patrón 2 dos veces, solo tenga en cuenta el hecho de que se repetirá en la expresión regular:
both = re.compile("{}({}){{2}}".format(pattern1, pattern2), re.IGNORECASE)que le da la siguiente expresión regular:
r'(hello?\s*)(one([-/])two([-/])three){2}'