2.36. Grupos y anclas¶
Un patrón puede hacer algo más que decir «esta cadena coincide»: puede separar las partes coincidentes y entregar cada una a la aplicación por su nombre. Los paréntesis alrededor de una parte de un patrón la convierten en un grupo de captura; el objeto de coincidencia expone entonces cada grupo como una subcadena independiente.
2.36.1. Grupos de captura¶
Envuelve cualquier parte de un patrón en (...) para capturar lo que coincidió:
>>> import re
>>> m = re.search(r'temp (\d+) at (\d+)s', 'temp 42 at 137s ok')
>>> m.group(0)
'temp 42 at 137s'
>>> m.group(1)
'42'
>>> m.group(2)
'137'
El grupo 0 es siempre la coincidencia completa.
Los grupos 1, 2, … son las subcadenas capturadas, numeradas de izquierda a derecha por su paréntesis de apertura.
Llamar a
match.group()con un índice posterior al último grupo provoca unIndexError.
Un patrón habitual es «coincidir con una estructura conocida y capturar las partes variables como enteros»:
def parse_temp(line):
m = re.search(r'temp (\d+) at (\d+)s', line)
if not m:
return None
return int(m.group(1)), int(m.group(2))
2.36.2. Grupos sin captura¶
Los paréntesis también agrupan una subexpresión para que un cuantificador pueda aplicarse a todo el grupo. Ese es el único propósito de la agrupación en r'(ab)+': «uno o más de ab». El hecho de que ab aparezca como grupo 1 es un efecto secundario.
Para agrupar sin capturar, usa (?:...):
>>> re.search(r'(?:ab)+', 'xababy').group(0)
'abab'
Los grupos sin captura mantienen los números de grupo ordenados cuando un patrón usa la agrupación para dar estructura pero no le interesa extraer cada parte.
2.36.3. Anclas¶
Las anclas no coinciden con un carácter: coinciden con una posición.
^– inicio de la cadena.$– final de la cadena.
Las anclas son lo que hace que re.match() y re.search() se comporten de forma diferente. re.match(p, s) es lo mismo que re.search('^' + p, s): obliga al patrón a empezar en la posición 0. Añadir $ al final de un patrón hace entonces que el patrón coincida con la cadena entera y nada más:
>>> re.search(r'^\d+$', '12345')
<match num=1>
>>> re.search(r'^\d+$', '12345 ok') is None
True
^ y $ en el módulo re de MicroPython siempre significan el inicio y el final de la cadena completa pasada a re.search(). No existe ninguna bandera re.MULTILINE que haga que coincidan en cada salto de línea incrustado, y $ tampoco coincide con la posición anterior a un \n final: tiene que ser el final absoluto de la entrada. Para obtener un comportamiento por línea, divide primero la entrada en saltos de línea y ejecuta el patrón en cada línea.
2.36.4. Conjuntos de caracteres¶
Los corchetes definen un conjunto explícito de caracteres. La coincidencia consume exactamente un carácter del conjunto.
[abc]– uno dea,b,c.[a-z]– un carácter en el rangoa-z(ambos incluidos).[a-zA-Z0-9]– letras o dígitos. Tres rangos combinados.[^abc]– no uno dea,b,c. El^solo niega cuando es el primer carácter dentro de los corchetes.
Ejemplos:
>>> re.search(r'[A-F0-9]{6}', 'colour #1a2b3c rest').group(0)
'1A2B3C'
>>> re.search(r'[A-F0-9]{6}', 'colour #1a2b3c rest') is None
True
La primera llamada devuelve None en la práctica porque el texto literal está en minúsculas. El módulo re de MicroPython no tiene ninguna bandera re.IGNORECASE: para coincidir sin distinguir mayúsculas y minúsculas, escribe ambos casos dentro del conjunto:
>>> re.search(r'[A-Fa-f0-9]{6}', 'colour #1a2b3c rest').group(0)
'1a2b3c'
Los atajos de clase (\d, \s, \w y sus formas negadas) también pueden usarse dentro de [...]: [\w-] significa «caracteres de palabra o un guion literal».
2.36.5. Cuantificadores codiciosos frente a perezosos¶
Los cuantificadores *, +, ? y {m,n} son codiciosos de forma predeterminada: coinciden con tantos caracteres como el resto del patrón siga permitiendo. A menudo eso es exactamente lo que se quiere; a veces no:
>>> re.search(r'<(.+)>', 'a <b> <c> d').group(1)
'b> <c'
El .+ codicioso capturó hasta el último >. Añadir ? hace que el cuantificador sea perezoso: coincide con lo menos posible:
>>> re.search(r'<(.+?)>', 'a <b> <c> d').group(1)
'b'
La forma perezosa se detiene en el primer >. Los cuantificadores perezosos aparecen constantemente al extraer delimitadores equilibrados de una cadena.
2.36.6. Retrorreferencias en la sustitución¶
re.sub() puede referirse a los grupos capturados en la cadena de reemplazo mediante \1, \2, … La sustitución reescribe cada coincidencia usando las partes capturadas:
>>> re.sub(r'(\d+)\.(\d+)', r'\2.\1', 'swap 12.34 and 5.6')
'swap 34.12 and 6.5'
Cada coincidencia captura dos números, y el reemplazo los intercambia. \g<1> es una sintaxis alternativa para lo mismo: útil cuando el siguiente carácter en el reemplazo es un dígito (r'\g<1>0' para añadir un cero literal al grupo 1 en lugar de leer «grupo 10»).
2.36.7. Lo que no está disponible¶
Un recordatorio de lo que el módulo re de MicroPython no admite, por si un patrón de CPython acaba aquí y te sorprende:
Anticipación
(?=...)y retrospección(?<=...)– no implementadas.Grupos con nombre
(?P<name>...)y retrorreferencias con nombre(?P=name)– no implementados.Constantes de banderas como
re.IGNORECASE,re.MULTILINE,re.DOTALL– no se respetan. Construye tú mismo el conjunto sin distinción de mayúsculas y minúsculas o divide previamente la entrada.Los métodos
match.groups(),match.span(),match.start()ymatch.end()están condicionados a un nivel de ROM que ninguna placa OpenMV de las distribuidas habilita. El código que dependa de ellos no se ejecutará en la cámara.
Con patrones, grupos y anclas, el conjunto de herramientas de expresiones regulares de la cámara es lo bastante pequeño para aprenderlo de una sentada y lo bastante completo para hacer todo, salvo el análisis sensible al contexto.