Expresiones regulares COMOS (HOWTO)¶
- Autor
A.M. Kuchling <amk@amk.ca>
Resumen
Este documento es un tutorial de introducción al uso de expresiones regulares en Python con el módulo re
. Proporciona una introducción más apacible que la sección correspondiente en la Referencia de la Biblioteca.
Introducción¶
Las expresiones regulares (llamadas RE, o regex, o patrones de regex) son esencialmente en un lenguaje de programación diminuto y altamente especializado incrustado dentro de Python y disponible a través del módulo re
. Usando este pequeño lenguaje, especificas las reglas para el conjunto de cadenas de caracteres posibles que deseas hacer coincidir; este conjunto puede contener frases en inglés, o direcciones de correo electrónico, o comandos TeX, o cualquier cosa que desee. A continuación, puede hacer preguntas como «¿Coincide esta cadena con el patrón?» o «¿Hay alguna coincidencia con el patrón en alguna parte de esta cadena?». También puede utilizar RE para modificar una cadena de caracteres o dividirla de varias formas.
Los patrones de expresiones regulares se compilan en una serie de códigos de bytes que luego son ejecutados por un motor de coincidencia escrito en C. Para un uso avanzado, puede ser necesario prestar mucha atención a cómo el motor ejecutará una RE dado y escribir la RE en un de cierta manera para producir un código de bytes que se ejecute más rápido. La optimización no se trata en este documento, porque requiere que tenga un buen conocimiento de los componentes internos del motor de coincidencia.
El lenguaje de expresiones regulares es relativamente pequeño y restringido, por lo que no todas las posibles tareas de procesamiento de cadenas de caracteres se pueden realizar utilizando expresiones regulares. También hay tareas que se pueden hacer con expresiones regulares, pero las expresiones resultan ser muy complicadas. En estos casos, es mejor que escriba código Python para realizar el procesamiento; Si bien el código Python será más lento que una expresión regular elaborada, probablemente también será más comprensible.
Patrones simples¶
Comenzaremos aprendiendo sobre las expresiones regulares más simples posibles. Dado que las expresiones regulares se utilizan para operar en cadenas de caracteres, comenzaremos con la tarea más común: hacer coincidir caracteres.
Para obtener una explicación detallada de la informática que subyace a las expresiones regulares (autómatas finitos deterministas y no deterministas), puede consultar casi cualquier libro de texto sobre la escritura de compiladores.
Coincidencia de Caracteres (Matching Characters)¶
La mayoría de letras y caracteres simplemente coincidirán. Por ejemplo, la expresión regular test
coincidirá exactamente con la cadena test
. (Puede habilitar un modo que no distinga entre mayúsculas y minúsculas que permitiría que este RE coincida con test
o TEST
también; más sobre esto más adelante.)
Hay excepciones a esta regla; algunos caracteres son especiales metacharacters, y no coinciden. En cambio, señalan que debe coincidir con algo fuera de lo común, o afectan otras partes de la RE repitiéndolos o cambiando su significado. Gran parte de este documento está dedicado a discutir varios metacarácteres y lo que hacen.
Aquí hay una lista completa de los metacarácteres; sus significados se discutirán en el resto de este COMO (HOWTO).
. ^ $ * + ? { } [ ] \ | ( )
Los primeros metacarácteres que veremos son [
and ]
. Se utilizan para especificar una clase de carácter, que es un conjunto de caracteres que desea hacer coincidir. Los caracteres se pueden enumerar individualmente, o se puede indicar un rango de caracteres dando dos caracteres y separándolos con un '-'
. Por ejemplo, [abc]
coincidirá con cualquiera de los caracteres a
, b
o c
; esto es lo mismo que [a-c]
, que usa un rango para expresar el mismo conjunto de caracteres. Si quisiera hacer coincidir solo letras minúsculas, su RE sería [a-c]
.
Los metacarácteres no están activos dentro de las clases. Por ejemplo, [akm$]
coincidirá con cualquiera de los caracteres 'a'
, 'k'
, 'm'
, or '$'
; '$'
suele ser un metacarácter, pero dentro de una clase de carácter se le quita su naturaleza especial.
Puede hacer coincidir los caracteres que no figuran en la clase mediante el conjunto complementing. Esto se indica mediante la inclusión de un '^'
como primer carácter de la clase. Por ejemplo, [^5]
coincidirá con cualquier carácter excepto con '5'
. Si el símbolo de intercalación aparece en otra parte de una clase de caracter, no tiene un significado especial. Por ejemplo: [5^] `` coincidirá con un ``'5'
o un '^'
.
Quizás el metacarácter más importante es la barra invertida, \
. Al igual que en los literales de cadena de Python, la barra invertida puede ir seguida de varios caracteres para señalar varias secuencias especiales. También se usa para escapar de todos los metacarácteres, de modo que aún pueda emparejarlos en patrones; por ejemplo, si necesita hacer coincidir un [
o \
, puede precederlos con una barra invertida para eliminar su significado especial: \[
o \\
.
Algunas de las secuencias especiales que comienzan con '\'
representan conjuntos predefinidos de caracteres que a menudo son útiles, como el conjunto de dígitos, el conjunto de letras o el conjunto de cualquier cosa que no sea un espacio en blanco.
Tomemos un ejemplo: \w
coincide con cualquier carácter alfanumérico. Si el patrón de expresiones regulares se expresa en bytes, esto es equivalente a la clase [a-zA-Z0-9_]
. Si el patrón de expresiones regulares es una cadena de caracteres, \w
coincidirá con todos los caracteres marcados como letras en la base de datos Unicode proporcionada por el módulo unicodedata
. Puede usar la definición más restringida de \w
en un patrón de cadena proporcionando el indicador re.ASCII
al compilar la expresión regular.
La siguiente lista de secuencias especiales no está completa. Para obtener una lista completa de secuencias y definiciones de clases expandidas para patrones de cadenas Unicode, consulte la última parte de Regular Expression Syntax en la referencia de la biblioteca estándar. En general, las versiones Unicode coinciden con cualquier carácter que esté en la categoría apropiada en la base de datos Unicode.
\d
Coincide con cualquier dígito decimal; esto es equivalente a la clase
[0-9]
.\D
Coincide con cualquier carácter que no sea un dígito; esto es equivalente a la clase
[^0-9]
.\s
Coincide con cualquier carácter de espacio en blanco; esto es equivalente a la clase
[ \t\n\r\f\v]
.\S
Coincide con cualquier carácter que no sea un espacio en blanco; esto es equivalente a la clase
[^ \t\n\r\f\v]
.\w
Coincide con cualquier carácter alfanumérico; esto es equivalente a la clase
[a-zA-Z0-9_]
.\W
Coincide con cualquier carácter no alfanumérico; esto es equivalente a la clase
[^a-zA-Z0-9_]
.
Estas secuencias se pueden incluir dentro de una clase de carácter. Por ejemplo, [\s,.]
es una clase de carácter que coincidirá con cualquier carácter de espacio en blanco, o ','
o '.'
.
El metacarácter final en esta sección es .
. Coincide con cualquier cosa excepto un carácter de nueva línea, y hay un modo alternativo (re.DOTALL
) donde coincidirá incluso con una nueva línea. .
se usa a menudo cuando se desea hacer coincidir «cualquier carácter».
Repitiendo cosas¶
Ser capaz de hacer coincidir diferentes conjuntos de caracteres es lo primero que pueden hacer las expresiones regulares que ya no es posible con los métodos disponibles en cadenas de caracteres. Sin embargo, si esa fuera la única capacidad adicional de las expresiones regulares, no serían un gran avance. Otra capacidad es que puede especificar que partes de la RE deben repetirse un cierto número de veces.
El primer metacarácter para repetir cosas que veremos es *
. *
no coincide con el carácter literal '*'
; en cambio, especifica que el carácter anterior puede coincidir cero o más veces, en lugar de exactamente una vez.
Por ejemplo, ca*t
coincidirá con 'ct'
(0 'a'
caracteres), 'cat'
(1 'a'
), 'caaat'
(3 'a'
caracteres), etc.
Las repeticiones como *
son greedy; al repetir una RE, el motor de emparejamiento intentará repetirlo tantas veces como sea posible. Si las partes posteriores del patrón no coinciden, el motor de coincidencia hará una copia de seguridad y volverá a intentarlo con menos repeticiones.
Un ejemplo paso a paso hará que esto sea más obvio. Consideremos la expresión a[bcd]*b
. Esto coincide con la letra 'a'
, cero o más letras de la clase [bcd]
, y finalmente termina con una 'b'
. Ahora imagina hacer coincidir este RE con la cadena de caracteres 'abcbd'
.
Pasos |
Coincidencias |
Explicación |
---|---|---|
1 |
|
La |
2 |
|
El motor coincide con |
3 |
Failure |
El motor intenta hacer coincidir |
4 |
|
Hace una copia de seguridad para que |
5 |
Failure |
Intente |
6 |
|
Haga una copia de seguridad de nuevo, de modo que |
6 |
|
Intente |
Se ha alcanzado el final de la RE y ha coincidido con 'abcb'
. Esto demuestra cómo el motor de coincidencias llega tan lejos como puede al principio, y si no se encuentra ninguna coincidencia, retrocederá progresivamente y volverá a intentar de la RE una y otra vez. Hará una copia de seguridad hasta que haya probado cero coincidencias para [bcd]*
, y si eso falla posteriormente, el motor concluirá que la cadena no coincide con la RE en absoluto.
Otro metacarácter que se repite es +
, que coincide una o más veces. Preste especial atención a la diferencia entre *
and +
; coincide con cero o más veces, por lo que cualquier cosa que se repita puede no estar presente en absoluto, mientras que +
requiere al menos one aparición. Para usar un ejemplo similar, 'cat'
(1 'a'
), 'caaat'
(3 'a'
s), pero no coincidirá con 'ct'
.
Hay dos calificadores más que se repiten. El carácter de signo de interrogación, ?
, Coincide una vez o cero veces; puede pensar en ello como si marcara algo como opcional. Por ejemplo, home-?brew
coincide con 'homebrew'
o 'home-brew'
.
El calificador repetido más complicado es {m,n}
, donde m y n son enteros decimales. Este calificador significa que debe haber al menos m repeticiones y como máximo n. Por ejemplo, a/{1,3}b
coincidirá con``”a/b”, ``'a//b'
, and 'a///b'
. No coincidirá con `` “ab” “, que no tiene barras, ni con 'a////b'
, que tiene cuatro.
Puede omitir m o n; en ese caso, se asume un valor razonable para el valor faltante. Omitir m se interpreta como un límite inferior de 0, mientras que omitir n da como resultado un límite superior de infinito.
Los lectores de una inclinación reduccionista pueden notar que los otros tres calificativos pueden expresarse usando esta notación. {0,}
es lo mismo que *
, {1,}
es equivalente a +
, y {0,1}
es lo mismo que ?
. Es mejor usar *
, +
, o ?
cuando pueda, simplemente porque son más cortos y fáciles de leer.
Usando Expresiones Regulares¶
Ahora que hemos visto algunas expresiones regulares simples, ¿cómo las usamos realmente en Python? El módulo re
proporciona una interfaz para el motor de expresiones regulares, lo que le permite compilar RE en objetos y luego realizar coincidencias con ellos.
Compilando Expresiones regulares¶
Regular expressions are compiled into pattern objects, which have methods for various operations such as searching for pattern matches or performing string substitutions.
>>> import re
>>> p = re.compile('ab*')
>>> p
re.compile('ab*')
re.compile()
también acepta un argumento opcional flags, usado para habilitar varias características especiales y variaciones de sintaxis. Repasaremos las configuraciones disponibles más adelante, pero por ahora un solo ejemplo servirá:
>>> p = re.compile('ab*', re.IGNORECASE)
La RE se pasa a re.compile()
como una cadena de caracteres. Las RE se manejan como cadenas de caracteres porque las expresiones regulares no son parte del lenguaje central de Python y no se creó una sintaxis especial para expresarlas. (Hay aplicaciones que no necesitan RE en absoluto, por lo que no hay necesidad de aumentar la especificación del lenguaje incluyéndolas). En cambio, el módulo re
es simplemente un módulo de extensión C incluido en Python, al igual que los módulos socket
o zlib
.
Poner RE en cadenas de caracteres mantiene el lenguaje Python más simple, pero tiene una desventaja que es el tema de la siguiente sección.
La plaga de la barra invertida (The Backslash Plague)¶
Como se indicó anteriormente, las expresiones regulares usan el carácter de barra invertida ('\'
) para indicar formas especiales o para permitir que se usen caracteres especiales sin invocar su significado especial. Esto entra en conflicto con el uso de Python del mismo carácter para el mismo propósito en cadenas literales.
Supongamos que desea escribir una RE que coincida con la cadena de caracteres \section
, que podría encontrarse en un archivo LaTeX. Para averiguar qué escribir en el código del programa, comience con la cadena deseada para que coincida. A continuación, debe escapar de las barras invertidas y otros metacarácteres precediéndolos con una barra invertida, lo que da como resultado la cadena \\section
. La cadena resultante que debe pasarse a re.compile()
debe ser \\section
. Sin embargo, para expresar esto como una cadena literal de Python, ambas barras invertidas deben escaparse nuevamente.
Caracteres |
Explicación |
---|---|
|
Cadena de texto que debe coincidir |
|
Barra invertida de escape para |
|
Barra invertida de escape para un literal de cadena de caracteres |
En resumen, para hacer coincidir una barra invertida literal, uno tiene que escribir '\\\\'
como la cadena RE, porque la expresión regular debe ser \\
, y cada barra invertida debe expresarse como \\
dentro de un literal de cadena Python normal. En las RE que presentan barras invertidas repetidamente, esto genera muchas barras invertidas repetidas y dificulta la comprensión de las cadenas resultantes.
La solución es utilizar la notación de cadena de caracteres sin formato de Python para expresiones regulares; las barras invertidas no se manejan de ninguna manera especial en una cadena literal con el prefijo 'r'
, por lo que r"\n"
es una cadena de dos caracteres que contiene '\'
y 'n'
, mientras que "\n"
es una cadena de un carácter que contiene una nueva línea. Las expresiones regulares a menudo se escribirán en código Python utilizando esta notación de cadena sin formato.
Además, las secuencias de escape especiales que son válidas en expresiones regulares, pero no válidas como literales de cadena de Python, ahora dan como resultado DeprecationWarning
y eventualmente se convertirán en SyntaxError
, lo que significa que las secuencias no serán válidas. si no se utiliza la notación de cadena sin formato o el escape de las barras invertidas.
Cadena de caracteres regulares |
Cadena de caracteres crudas (Raw string) |
---|---|
|
|
|
|
|
|
Realizando coincidencias¶
Una vez que tiene un objeto que representa una expresión regular compilada, ¿qué hace con él? Los objetos de patrón tienen varios métodos y atributos. Aquí solo se cubrirán los más importantes; consulte los documentos re
para obtener una lista completa.
Método/atributo |
Objetivo |
---|---|
|
Determina si la RE coincide con el comienzo de la cadena de caracteres. |
|
Escanea una cadena, buscando cualquier ubicación donde coincida este RE. |
|
Encuentra todas las subcadenas de caracteres donde coincide la RE y las retorna como una lista. |
|
Encuentra todas las subcadenas donde la RE coincide y las retorna como un término iterado iterator. |
match()
y search()
retornan None
si la coincidencia no puede ser encontrada. Si tienen éxito, se retorna una instancia match object, que contiene información sobre la coincidencia: dónde comienza y termina, la subcadena de caracteres con la que coincidió, y más.
Puede aprender sobre esto experimentando interactivamente con el módulo re
. Si tiene tkinter
disponible, también puede consultar Tools/demo/redemo.py, un programa de demostración incluido con la distribución de Python. Le permite ingresar RE y cadenas de caracteres, y muestra si la RE coincide o falla. redemo.py
puede ser bastante útil cuando se intenta depurar una RE complicado.
Este CÓMO (HOWTO) utiliza el intérprete estándar de Python para sus ejemplos. Primero, ejecute el intérprete de Python, importe el módulo re
y compile en RE:
>>> import re
>>> p = re.compile('[a-z]+')
>>> p
re.compile('[a-z]+')
Ahora, puede intentar hacer coincidir varias cadenas con la RE [a-z]+
. Una cadena de caracteres vacía no debería coincidir en absoluto, ya que +
significa que “una o más repeticiones”. match()
debería retornar None
en este caso, lo que hará que el intérprete no imprima ningún resultado. Puede imprimir explícitamente el resultado de match()
para aclarar esto.
>>> p.match("")
>>> print(p.match(""))
None
Ahora, intentémoslo en una cadena de caracteres que debería coincidir, como tempo
.En este caso , match()
retornará un match object, por lo que debe almacenar el resultado en una variable para su posterior uso.
>>> m = p.match('tempo')
>>> m
<re.Match object; span=(0, 5), match='tempo'>
Ahora puede consultar match object para obtener información sobre la cadena coincidente. Las instancias de objetos coincidentes también tienen varios métodos y atributos; los más importantes son:
Método/atributo |
Objetivo |
---|---|
|
Retorna la cadena de caracteres que coincide con la RE |
|
Retorna la posición de inicio de la coincidencia |
|
Retorna la posición final de la coincidencia |
|
Retorna una tupla que contiene (inicio, final) las posiciones de coincidencia |
Probando estos métodos pronto aclarará sus significados:
>>> m.group()
'tempo'
>>> m.start(), m.end()
(0, 5)
>>> m.span()
(0, 5)
group()
retorna la subcadena de caracteres que coincide con la RE. start()
y end()
retornan el índice inicial y final de la coincidencia. span()
retorna el índice inicial y final en una única tupla. Dado que el método match()
solo verifica si la RE coincide al comienzo de una cadena de caracteres, start()
siempre será cero. Sin embargo, el método de patrones search()
escanea a través de la cadena de caracteres, por lo que es posible que la coincidencia no comience en cero en ese caso.
>>> print(p.match('::: message'))
None
>>> m = p.search('::: message'); print(m)
<re.Match object; span=(4, 11), match='message'>
>>> m.group()
'message'
>>> m.span()
(4, 11)
En programas reales, el estilo más común es almacenar match object en una variable, y luego verificar si era None
. Esto generalmente se ve así:
p = re.compile( ... )
m = p.match( 'string goes here' )
if m:
print('Match found: ', m.group())
else:
print('No match')
Dos métodos de patrón retornan todas las coincidencias de un patrón. findall()
retorna una lista de cadenas de caracteres coincidentes:
>>> p = re.compile(r'\d+')
>>> p.findall('12 drummers drumming, 11 pipers piping, 10 lords a-leaping')
['12', '11', '10']
El prefijo r
, que convierte al literal en una cadena de caracteres literal sin formato, es necesario en este ejemplo porque las secuencias de escape en un cadena de caracteres literal «cocinado» normal que no son reconocidas por Python, a diferencia de las expresiones regulares, ahora dan como resultado DeprecationWarning
y eventualmente se convertirá en SyntaxError
. Ver La plaga de la barra invertida (The Backslash Plague).
findall()
tiene que crear la lista completa antes de que pueda retornarse como resultado. El método finditer()
retorna una secuencia de match object instancias como iterados iterator:
>>> iterator = p.finditer('12 drummers drumming, 11 ... 10 ...')
>>> iterator
<callable_iterator object at 0x...>
>>> for match in iterator:
... print(match.span())
...
(0, 2)
(22, 24)
(29, 31)
Funciones a nivel de módulo¶
No es necesario crear un objeto patrón y llamar a sus métodos; el módulo re
también proporciona funciones de nivel superior llamadas match()
, search()
, findall()
, sub()
, y así sucesivamente. Estas funciones toman los mismos argumentos que el método de patrón correspondiente con la cadena de RE agregada como primer argumento, y aún así retornan una instancia de None
o match object.
>>> print(re.match(r'From\s+', 'Fromage amk'))
None
>>> re.match(r'From\s+', 'From amk Thu May 14 19:12:10 1998')
<re.Match object; span=(0, 5), match='From '>
Bajo el capó (hood), estas funciones simplemente crean un objeto patrón para usted y llaman al método apropiado en él. También almacenan el objeto compilado en un caché, por lo que las futuras llamadas que usen el mismo RE no necesitarán analizar el patrón una y otra vez.
¿Debería utilizar estas funciones a nivel de módulo o debería obtener el patrón y llamar a sus métodos usted mismo? Si está accediendo a una expresión regular dentro de un bucle, la compilación previa guardará algunas llamadas a funciones. Fuera de los bucles, no hay mucha diferencia gracias al caché interno.
Los Flags de compilación¶
Las flags de compilación le permiten modificar algunos aspectos de cómo funcionan las expresiones regulares. Las flags están disponibles en el módulo re
con dos nombres, un nombre largo como IGNORECASE
y una forma corta de una letra como I
. (Si está familiarizado con los modificadores de patrones de Perl, las formas de una letra usan las mismas letras; la forma corta de re.VERBOSE
es re.X
, por ejemplo).Se pueden especificar varios indicadores uniéndolos con OR bit a bit; re.I | re.M
establece los flags I
and M
, por ejemplo.
Aquí hay una tabla de las flags disponibles, seguida de una explicación más detallada de cada una.
Flag |
Significado |
---|---|
|
Hace que varios escapes como |
|
Hace que |
|
Hace coincidencias que no distingan entre mayúsculas y minúsculas. |
|
Hace una coincidencia con reconocimiento de configuración regional. |
|
Coincidencia de varias líneas, que afecta a |
|
Habilite RE detallados, que se pueden organizar de manera más limpia y comprensible. |
-
I
-
IGNORECASE
Realiza una coincidencia que no distinga entre mayúsculas y minúsculas; la clase de caracteres y las cadenas de caracteres literales coincidirán con las letras ignorando las mayúsculas. Por ejemplo,
[A-Z]
también coincidirá con letras minúsculas. La coincidencia completa de Unicode también funciona a menos que se utilice la flagASCII
para deshabilitar las coincidencias que no sean ASCII. Cuando los patrones Unicode[a-z]
o[A-Z]
se utilizan en combinación con el indicadorIGNORECASE
, coincidirán con las 52 letras ASCII y 4 letras adicionales no ASCII “İ” (U+0130, letra mayúscula latina I con un punto arriba), “ı” (U+0131, letra minúscula latina sin punto i),”ſ” (U+017F, letra minúscula latina larga s) y “K” (U+212A, signo de Kelvin).Spam
coincidirá'Spam'
,'spam'
,'spAM'
, o'ſpam'
(este último solo coincide en modo Unicode). Estas minúsculas no tiene en cuenta la configuración regional actual; lo hará si también establece la flagLOCALE
.
-
L
-
LOCALE
Hace que
\w
,\W
,\b
,\B
y la coincidencia que no distinga entre mayúsculas y minúsculas dependan de la configuración regional actual en lugar de la base de datos Unicode.Las configuraciones regionales son una característica de la biblioteca C destinada a ayudar a escribir programas que tengan en cuenta las diferencias de idioma. Por ejemplo, si está procesando texto en francés codificado, querrá poder escribir
\w+
para que coincida con las palabras, pero\w
solo coincide con la clase de caracteres[A-Za-z]
en patrones de bytes; no coincidirá con los bytes correspondientes aé
orç
. Si su sistema está configurado correctamente y se selecciona una configuración regional francesa, ciertas funciones de C le dirán al programa que el byte correspondiente aé
también debe considerarse una letra. Establecer el indicadorLOCALE
al compilar una expresión regular hará que el objeto compilado resultante use estas funciones C para\w
; esto es más lento, pero también permite que\w+
coincida con las palabras en francés como era de esperar. Se desaconseja el uso de esta flag en Python 3 ya que el mecanismo de configuración regional es muy poco confiable, solo maneja una «cultura» a la vez y solo funciona con configuraciones regionales de 8 bits. La coincidencia Unicode ya está habilitada de forma predeterminada en Python 3 para patrones Unicode (str), y puede manejar diferentes configuraciones regionales/idiomas.
-
M
-
MULTILINE
(
^
y$
aún no se han explicado; se presentarán en la sección Más metacarácteres.)Por lo general,
^
coincide solo al principio de la cadena de caracteres, y$
solo coincide con el final de la cadena de caracteres e inmediatamente antes del salto de línea (si existe) al final de la cadena de caracteres. Cuando se especifica esta bandera,^
coincide al principio de la cadena y al comienzo de cada línea dentro de la cadena, inmediatamente después de cada nueva línea. De manera similar, el metacarácter$
coincide al final de la cadena de caracteres y al final de cada línea (inmediatamente antes de cada nueva línea).
-
S
-
DOTALL
Hace que el carácter especial
'.'
coincida con cualquier carácter, incluida una nueva línea; sin esta bandera,'.'
coincidirá con cualquier cosa except una nueva línea.
-
A
-
ASCII
Haga que
\w
,\W
,\b
,\B
,\s
y\S
realicen una coincidencia solo en ASCII en lugar de una coincidencia Unicode completa. Esto solo es significativo para los patrones Unicode y se ignora para los patrones de bytes.
-
X
-
VERBOSE
Esta flag le permite escribir expresiones regulares que son más legibles al otorgarle más flexibilidad en cómo puede formatearlas. Cuando se ha especificado esta flag, los espacios en blanco dentro de la cadena de caracteres de la RE se ignoran, excepto cuando el espacio en blanco está en una clase de caracteres o está precedido por una barra invertida sin escape; esto le permite organizar e indentar la RE más claramente. Esta flag también le permite poner comentarios dentro de una RE que serán ignorados por el motor (engine); los comentarios están marcados con un
'#'
que no está en una clase de carácter ni está precedido por una barra invertida sin escape.Por ejemplo, aquí hay una RE que usa
re.VERBOSE
; ¿Ves lo fácil que es leer?charref = re.compile(r""" &[#] # Start of a numeric entity reference ( 0[0-7]+ # Octal form | [0-9]+ # Decimal form | x[0-9a-fA-F]+ # Hexadecimal form ) ; # Trailing semicolon """, re.VERBOSE)
Sin la configuración detallada, la RE se vería así:
charref = re.compile("&#(0[0-7]+" "|[0-9]+" "|x[0-9a-fA-F]+);")
En el ejemplo anterior, la concatenación automática de cadenas de caracteres literales de Python se ha utilizado para dividir la RE en partes más pequeñas, pero aún es más difícil de entender que la versión que usa
re.VERBOSE
.
Más poder de patrones¶
Hasta ahora solo hemos cubierto una parte de las características de las expresiones regulares. En esta sección, cubriremos algunos metacarácteres nuevos y cómo usar grupos para recuperar partes del texto que coincidió.
Más metacarácteres¶
Hay algunos metacarácteres que aún no hemos cubierto. La mayoría de ellos se tratarán en esta sección.
Algunos de los metacarácteres restantes que se discutirán son zero-width assertions. No hacen que el motor avance a través de la cadena de caracteres; en cambio, no consumen caracteres en absoluto y simplemente tienen éxito o fracasan. Por ejemplo, \b
es una flag de que la posición actual se encuentra en el límite de una palabra; la posición no cambia por la \b
en absoluto. Esto significa que las aserciones de ancho cero nunca deben repetirse, porque si coinciden una vez en una ubicación determinada, obviamente pueden coincidir un número infinito de veces.
|
Alternancia, o el operador «or». Si A y B son expresiones regulares,
A|B
coincidirá con cualquier cadena de caracteres que coincida con A o B.|
tiene una precedencia muy baja para que funcione razonablemente cuando está alternando cadenas de varios caracteres.Crow|Servo
coincidirá con “Crow”` o'Servo'
, no'Cro'
, un'w'
o un'S'
, y'ervo'
.Para hacer coincidir un literal
'|'
, use\|
, o enciérrelo dentro de una clase de carácter, como en[|]
.^
Coincide con el comienzo de las líneas. A menos que se haya establecido la flag
MULTILINE
, esto solo coincidirá al principio de la cadena de caracteres. En modoMULTILINE
, esto también coincide inmediatamente después de cada nueva línea dentro de la cadena.Por ejemplo, si desea hacer coincidir la palabra
From
solo al principio de una línea, la RE que debe usar es^From
.>>> print(re.search('^From', 'From Here to Eternity')) <re.Match object; span=(0, 4), match='From'> >>> print(re.search('^From', 'Reciting From Memory')) None
Para una coincidencia literal
'^'
, usar\^
.$
Coincide con el final de una línea, que se define como el final de la cadena o cualquier ubicación seguida de un carácter de nueva línea.
>>> print(re.search('}$', '{block}')) <re.Match object; span=(6, 7), match='}'> >>> print(re.search('}$', '{block} ')) None >>> print(re.search('}$', '{block}\n')) <re.Match object; span=(6, 7), match='}'>
Para hacer coincidir un literal
'$'
, usar\$
o enciérrelo dentro de una clase de carácter, como en[$]
.\A
Coincide solo al comienzo de la cadena de caracteres. Cuando no está en el modo
MULTILINE
,``A`` y^
son efectivamente lo mismo. En el modoMULTILINE
, son diferentes:\A
todavía coincide solo al principio de la cadena, pero^
puede coincidir en cualquier ubicación dentro de la cadena de caracteres que sigue a un carácter de nueva línea.\Z
Coincidencias solo al final de la cadena de caracteres.
\b
Límite de palabra. Esta es una aserción de ancho cero que coincide solo al principio o al final de una palabra. Una palabra se define como una secuencia de caracteres alfanuméricos, por lo que el final de una palabra se indica mediante un espacio en blanco o un carácter no alfanumérico.
El siguiente ejemplo coincide con
class
solo cuando es una palabra completa; no coincidirá cuando esté contenido dentro de otra palabra.>>> p = re.compile(r'\bclass\b') >>> print(p.search('no class at all')) <re.Match object; span=(3, 8), match='class'> >>> print(p.search('the declassified algorithm')) None >>> print(p.search('one subclass is')) None
Hay dos sutilezas que debe recordar al usar esta secuencia especial. Primero, esta es la peor colisión entre las cadenas literales de caracteres de Python y las secuencias de expresiones regulares. En las cadenas de caracteres literales de Python,
\b
es el carácter de retroceso (backspace), valor ASCII 8. Si no está utilizando cadenas de caracteres sin procesar, Python convertirá la\b
en una linea de retroceso, y su RE no lo hará coincidir como lo espera. El siguiente ejemplo tiene el mismo aspecto que nuestra RE anterior, pero omite la'r'
delante de la cadena de caracteres de RE.>>> p = re.compile('\bclass\b') >>> print(p.search('no class at all')) None >>> print(p.search('\b' + 'class' + '\b')) <re.Match object; span=(0, 7), match='\x08class\x08'>
En segundo lugar, dentro de una clase de caracteres, donde no hay uso para esta aserción,
\b
representa el carácter de retroceso, por compatibilidad con las cadenas de caracteres literales de Python.\B
Otra flag de ancho cero, esto es lo opuesto a
\b
, solo coincide cuando la posición actual no está en el límite de una palabra.
Agrupando¶
Con frecuencia, necesita obtener más información que solo si la RE coincide o no. Las expresiones regulares se utilizan a menudo para diseccionar cadenas de caracteres escribiendo una RE dividido en varios subgrupos que coinciden con diferentes componentes de interés. Por ejemplo, una línea de encabezado RFC-822 se divide en un nombre de encabezado y un valor, separados por un ':'
, así:
From: author@example.com
User-Agent: Thunderbird 1.5.0.9 (X11/20061227)
MIME-Version: 1.0
To: editor@example.com
Esto se puede manejar escribiendo una expresión regular que coincida con una línea de encabezado completa y que tenga un grupo que coincida con el nombre del encabezado y otro grupo que coincida con el valor del encabezado.
Los grupos están marcados por los '('
, ')'
metacarácteres. '('
y ')'
tienen el mismo significado que en las expresiones matemáticas; agrupan las expresiones contenidas en ellos, y puedes repetir el contenido de un grupo con un calificador repetitivo, como *
, +
, ?
, o {m,n}
. Por ejemplo, (ab)*
coincidirá con cero o más repeticiones de ab
.
>>> p = re.compile('(ab)*')
>>> print(p.match('ababababab').span())
(0, 10)
Los grupos indicados con '('
, ')'
también capturan el índice inicial y final del texto que coinciden; esto se puede recuperar pasando un argumento a group()
, start()
, end()
, y span()
. Los grupos se numeran empezando por 0. El grupo 0 siempre está presente; es todo las RE, entonces todos los métodos match object tienen el grupo 0 como argumento predeterminado. Más adelante veremos cómo expresar grupos que no capturan el espacio de texto que coinciden.
>>> p = re.compile('(a)b')
>>> m = p.match('ab')
>>> m.group()
'ab'
>>> m.group(0)
'ab'
Los subgrupos están numerados de izquierda a derecha, de 1 en adelante. Los grupos se pueden anidar; para determinar el número, simplemente cuente los caracteres del paréntesis de apertura, de izquierda a derecha.
>>> p = re.compile('(a(b)c)d')
>>> m = p.match('abcd')
>>> m.group(0)
'abcd'
>>> m.group(1)
'abc'
>>> m.group(2)
'b'
group()
se pueden pasar varios números de grupo a la vez, en cuyo caso retornará una tupla que contiene los valores correspondientes para esos grupos.
>>> m.group(2,1,2)
('b', 'abc', 'b')
El método groups()
retorna una tupla que contiene las cadenas de caracteres de todos los subgrupos, desde 1 hasta la cantidad que haya.
>>> m.groups()
('abc', 'b')
Las referencias inversas en un patrón le permiten especificar que el contenido de un grupo de captura anterior también debe encontrarse en la ubicación actual en la cadena. Por ejemplo,``1`` tendrá éxito si el contenido exacto del grupo 1 se puede encontrar en la posición actual y falla en caso contrario. Recuerde que las cadenas de caracteres literales de Python también usan una barra invertida seguida de números para permitir la inclusión de caracteres arbitrarios en una cadena de caracteres, así que asegúrese de usar una cadena de caracteres sin procesar al incorporar referencias inversas en una RE.
Por ejemplo, la siguiente RE detecta palabras duplicadas en una cadena.
>>> p = re.compile(r'\b(\w+)\s+\1\b')
>>> p.search('Paris in the the spring').group()
'the the'
Las referencias inversas como esta no suelen ser útiles para buscar a través de una cadena — hay pocos formatos de texto que repiten datos de esta manera — pero pronto descubrirá que son muy útiles al realizar sustituciones de cadenas de caracteres.
Grupos con nombre y sin captura¶
Las RE elaboradas pueden utilizar muchos grupos, tanto para capturar subcadenas de caracteres de interés como para agrupar y estructurar la propia RE. En las RE complejas, resulta difícil realizar un seguimiento de los números de los grupos. Hay dos funciones que ayudan con este problema. Ambos usan una sintaxis común para las extensiones de expresiones regulares, así que veremos eso primero.
Perl 5 es bien conocido por sus poderosas adiciones a las expresiones regulares estándar. Para estas nuevas características, los desarrolladores de Perl no podían elegir nuevos metacarácteres de una sola pulsación de tecla o nuevas secuencias especiales que comienzan con \
sin hacer que las expresiones regulares de Perl sean confusamente diferentes de las RE estándar. Si eligieran &
como un nuevo metacarácter, por ejemplo, las expresiones antiguas supondrían que &
era un carácter regular y no se habría escapado escribiendo \&
o [&]
.
La solución elegida por los desarrolladores de Perl fue utilizar (?...)
como sintaxis de extensión. ?
inmediatamente después de un paréntesis había un error de sintaxis porque el ?
no tendría nada que repetir, por lo que esto no introdujo ningún problema de compatibilidad. Los caracteres inmediatamente después de ?
Indican qué extensión se está utilizando, por lo que (?=foo)
es una cosa (una flag de anticipación positiva) y (?:foo)
es otra cosa (un grupo de no captura que contiene la subexpresión foo
).
Python admite varias de las extensiones de Perl y agrega una sintaxis de extensión a la sintaxis de extensión de Perl. Si el primer carácter después del signo de interrogación es una P
, sabrá que es una extensión específica de Python.
Ahora que hemos visto la sintaxis de la extensión general, podemos volver a las características que simplifican el trabajo con grupos en RE complejos.
A veces querrá usar un grupo para denotar una parte de una expresión regular, pero no está interesado en recuperar el contenido del grupo. Puede hacer que este hecho sea explícito utilizando un grupo de no captura: (?:...)
, donde puede reemplazar el ...
con cualquier otra expresión regular.
>>> m = re.match("([abc])+", "abc")
>>> m.groups()
('c',)
>>> m = re.match("(?:[abc])+", "abc")
>>> m.groups()
()
Excepto por el hecho de que no puede recuperar el contenido de lo que coincide con el grupo, un grupo que no captura se comporta exactamente igual que un grupo que captura; puede poner cualquier cosa dentro de él, repetirlo con un metacarácter de repetición como *
y anidarlo dentro de otros grupos (capturando o no capturando). (?:...)
es particularmente útil cuando se modifica un patrón existente, ya que puede agregar nuevos grupos sin cambiar cómo se numeran todos los demás grupos. Cabe mencionar que no hay diferencia de rendimiento en la búsqueda entre grupos de captura y no captura; ninguna forma es más rápida que la otra.
Una característica más significativa son los grupos nombrados: en lugar de referirse a ellos por números, los grupos pueden ser referenciados por un nombre.
La sintaxis de un grupo con nombre es una de las extensiones específicas de Python: (?P<name>...)
. name es, obviamente, el nombre del grupo. Los grupos con nombre se comportan exactamente como los grupos de captura y, además, asocian un nombre con un grupo. Los métodos match object que tratan con la captura de grupos aceptan enteros que se refieren al grupo por número o cadenas de caracteres que contienen el nombre del grupo deseado. Los grupos con nombre todavía reciben números, por lo que puede recuperar información sobre un grupo de dos maneras:
>>> p = re.compile(r'(?P<word>\b\w+\b)')
>>> m = p.search( '(((( Lots of punctuation )))' )
>>> m.group('word')
'Lots'
>>> m.group(1)
'Lots'
Además, puede recuperar grupos nombrados como un diccionario con groupdict()
:
>>> m = re.match(r'(?P<first>\w+) (?P<last>\w+)', 'Jane Doe')
>>> m.groupdict()
{'first': 'Jane', 'last': 'Doe'}
Los grupos con nombre son útiles porque le permiten usar nombres fáciles de recordar, en lugar de tener que recordar números. Aquí hay un ejemplo de RE del módulo imaplib
:
InternalDate = re.compile(r'INTERNALDATE "'
r'(?P<day>[ 123][0-9])-(?P<mon>[A-Z][a-z][a-z])-'
r'(?P<year>[0-9][0-9][0-9][0-9])'
r' (?P<hour>[0-9][0-9]):(?P<min>[0-9][0-9]):(?P<sec>[0-9][0-9])'
r' (?P<zonen>[-+])(?P<zoneh>[0-9][0-9])(?P<zonem>[0-9][0-9])'
r'"')
Obviamente, es mucho más fácil recuperar m.group('zonem')
, en lugar de tener que recordar recuperar el grupo 9.
La sintaxis de las referencias inversas en una expresión como (...)\1
se refiere al número del grupo. Naturalmente, existe una variante que usa el nombre del grupo en lugar del número. Esta es otra extensión de Python: (?P=name)
indica que el contenido del grupo llamado name debe coincidir nuevamente en el punto actual. La expresión regular para encontrar palabras duplicadas, \b(\w+)\s+\1\b
también se puede escribir como \b(?P<word>\w+)\s+(?P=word)\b
:
>>> p = re.compile(r'\b(?P<word>\w+)\s+(?P=word)\b')
>>> p.search('Paris in the the spring').group()
'the the'
Aserciones Anticipadas¶
Otra aserción de ancho cero es la aserción de anticipación. Las afirmaciones anticipadas están disponibles tanto en forma positiva como negativa, y tienen este aspecto:
(?=...)
Aserción de anticipación positiva. Esto tiene éxito si la expresión regular contenida, representada aquí por
...
, coincide con éxito en la ubicación actual y falla en caso contrario. Pero, una vez que se ha probado la expresión contenida, el motor de comparación no avanza en absoluto; el resto del patrón se intenta justo donde comenzó la aserción.(?!...)
Aserción de anticipación negativa. Esto es lo opuesto a la flag positiva; tiene éxito si la expresión contenida no coincide con la posición actual en la cadena.
Para que esto sea concreto, veamos un caso en el que una búsqueda anticipada es útil. Considere un patrón simple para hacer coincidir un nombre de archivo y dividirlo en un nombre base y una extensión, separados por un .
. Por ejemplo, en news.rc
, news
es el nombre base y rc
es la extensión del nombre del archivo.
El patrón para que coincida con esto es bastante simple:
.*[.].*$
Tenga en cuenta que el .
Debe tratarse especialmente porque es un metacarácter, por lo que está dentro de una clase de carácter para coincidir solo con ese carácter específico. También observe el final $
; esto se agrega para garantizar que todo el resto de la cadena deba incluirse en la extensión. Esta expresión regular coincide con foo.bar
y autoexec.bat
y sendmail.cf
y printers.conf
.
Ahora, considere complicar un poco el problema; ¿Qué sucede si desea hacer coincidir los nombres de archivo donde la extensión no es bat
? Algunos intentos incorrectos:
.*[.][^b].*$
El primer intento anterior intenta excluir bat
requiriendo que el primer carácter de la extensión no sea una b
. Esto está mal, porque el patrón tampoco coincide foo.bar
.
.*[.]([^b]..|.[^a].|..[^t])$
La expresión se vuelve más desordenada cuando intenta parchear la primera solución requiriendo que coincida uno de los siguientes casos: el primer carácter de la extensión no es b
; el segundo carácter no es a
; o el tercer carácter no es t
. Esto acepta foo.bar
y rechaza autoexec.bat
, pero requiere una extensión de tres letras y no acepta un nombre de archivo con una extensión de dos letras como sendmail.cf
. Complicaremos el patrón nuevamente en un esfuerzo por arreglarlo.
.*[.]([^b].?.?|.[^a]?.?|..?[^t]?)$
En el tercer intento, la segunda y tercera letras se hacen opcionales para permitir extensiones coincidentes de menos de tres caracteres, como sendmail.cf
.
El patrón se está volviendo realmente complicado ahora, lo que dificulta su lectura y comprensión. Peor aún, si el problema cambia y desea excluir tanto bat
y exe
como extensiones, el patrón se volvería aún más complicado y confuso.
Una mirada anticipada negativa atraviesa toda esta confusión:
.*[.](?!bat$)[^.]*$
La búsqueda anticipada negativa significa: si la expresión bat
no coincide en este punto, pruebe el resto del patrón; si bat$
coincide, todo el patrón fallará. El $
final es necesario para garantizar que se permita algo como sample.batch
, donde la extensión solo comienza con bat
. El [^.]*
asegura que el patrón funcione cuando hay varios puntos en el nombre del archivo.
Ahora es fácil excluir otra extensión de nombre de archivo; simplemente agréguelo como una alternativa dentro de la aserción. El siguiente patrón excluye los nombres de archivo que terminan en bat
o exe
:
.*[.](?!bat$|exe$)[^.]*$
Modificando cadenas de caracteres¶
Hasta este punto, simplemente hemos realizado búsquedas en una cadena de caracteres estática. Las expresiones regulares también se utilizan comúnmente para modificar cadenas de varias formas, utilizando los siguientes métodos de patrón:
Método/atributo |
Objetivo |
---|---|
|
Divida la cadena de caracteres en una lista, dividiéndola donde coincida la RE |
|
Encuentra todas las subcadenas de caracteres donde coincida la RE y las reemplaza con una cadena de caracteres diferente |
|
Hace lo mismo que |
Separando cadenas de caracteres¶
El método split()
de un patrón que divide una cadena de caracteres donde la RE coincide, retornando una lista de las piezas. Es similar al método de cadenas de caracteres split()
pero proporciona mucha más generalidad en los delimitadores por los que puede dividir; cadena de caracteres split()
solo admite la división por espacios en blanco o por una cadena fija. Como era de esperar, también hay una función a nivel de módulo re.split()
.
-
.
split
(string[, maxsplit=0]) Dividir string por las coincidencias de la expresión regular. Si se utilizan paréntesis de captura en la RE, su contenido también se retornará como parte de la lista resultante. Si maxsplit es distinto de cero, se realizan como máximo divisiones maxsplit .
Puede limitar el número de divisiones realizadas, pasando un valor para maxsplit. Cuando maxsplit es distinto de cero, se realizarán como máximo maxsplit divisiones, y el resto de la cadena de caracteres se retorna como el elemento final de la lista. En el siguiente ejemplo, el delimitador es cualquier secuencia de caracteres no alfanuméricos.
>>> p = re.compile(r'\W+')
>>> p.split('This is a test, short and sweet, of split().')
['This', 'is', 'a', 'test', 'short', 'and', 'sweet', 'of', 'split', '']
>>> p.split('This is a test, short and sweet, of split().', 3)
['This', 'is', 'a', 'test, short and sweet, of split().']
A veces, no solo le interesa cuál es el texto entre delimitadores, sino que también necesita saber cuál era el delimitador. Si se utilizan paréntesis de captura en la RE, sus valores también se retornan como parte de la lista. Compare las siguientes llamadas:
>>> p = re.compile(r'\W+')
>>> p2 = re.compile(r'(\W+)')
>>> p.split('This... is a test.')
['This', 'is', 'a', 'test', '']
>>> p2.split('This... is a test.')
['This', '... ', 'is', ' ', 'a', ' ', 'test', '.', '']
La función de nivel de módulo re.split()
agrega la RE que se usará como primer argumento, pero por lo demás es el mismo.
>>> re.split(r'[\W]+', 'Words, words, words.')
['Words', 'words', 'words', '']
>>> re.split(r'([\W]+)', 'Words, words, words.')
['Words', ', ', 'words', ', ', 'words', '.', '']
>>> re.split(r'[\W]+', 'Words, words, words.', 1)
['Words', 'words, words.']
Búsqueda y Remplazo¶
Otra tarea común es encontrar todas las coincidencias para un patrón y reemplazarlas con una cadena de caracteres diferente. El método sub()
toma un valor de reemplazo, que puede ser una cadena de caracteres o una función, y la cadena de caracteres a procesar.
-
.
sub
(replacement, string[, count=0]) Retorna la cadena de caracteres obtenida al reemplazar las apariciones no superpuestas del extremo izquierdo de la RE en string por el remplazo replacement. Si no se encuentra el patrón, el string se retorna sin cambios.
El argumento opcional count es el número máximo de ocurrencias de patrones que se reemplazarán; count debe ser un número entero no negativo. El valor predeterminado de 0 significa reemplazar todas las ocurrencias.
Aquí hay un ejemplo simple del uso del método sub()
. Reemplaza los nombres de los colores con la palabra colour
:
>>> p = re.compile('(blue|white|red)')
>>> p.sub('colour', 'blue socks and red shoes')
'colour socks and colour shoes'
>>> p.sub('colour', 'blue socks and red shoes', count=1)
'colour socks and red shoes'
El método subn()
hace el mismo trabajo, pero retorna una tupla de 2 que contiene el nuevo valor de cadena de caracteres y el número de reemplazos que se realizaron:
>>> p = re.compile('(blue|white|red)')
>>> p.subn('colour', 'blue socks and red shoes')
('colour socks and colour shoes', 2)
>>> p.subn('colour', 'no colours at all')
('no colours at all', 0)
Las coincidencias vacías se reemplazan solo cuando no son adyacentes a una coincidencia vacía anterior.
>>> p = re.compile('x*')
>>> p.sub('-', 'abxd')
'-a-b--d-'
Si replacement es una cadena, se procesan los escapes de barra invertida que contenga. Es decir, \n
se convierte en un solo carácter de nueva línea, \r
se convierte en una REtorno de carro, y así sucesivamente. Los escapes desconocidos como \&
se dejan en paz. Las referencias inversas, como \6
, se reemplazan con la subcadena de caracteres que coincide con el grupo correspondiente a la RE. Esto le permite incorporar partes del texto original en la cadena de reemplazo resultante.
Este ejemplo hace coincidir la palabra section
seguida de una cadena encerrada entre {
, }
, y cambia section
a subsection
:
>>> p = re.compile('section{ ( [^}]* ) }', re.VERBOSE)
>>> p.sub(r'subsection{\1}','section{First} section{second}')
'subsection{First} subsection{second}'
También hay una sintaxis para hacer referencia a grupos con nombre según lo definido por la sintaxis (?P<name>…)``syntax. ``\g<name>
usará la subcadena de caracteres que coincide con el grupo llamado name
, y \g<number>
usa el número de grupo correspondiente. \g<2>
es equivalente a \2
, pero no es ambiguo en una cadena de reemplazo como \g<2>0
. (\20
se interpretaría como una referencia al grupo 20, no como una referencia al grupo 2 seguido del carácter literal '0'
.) Las siguientes sustituciones son todas equivalentes, pero use las tres variaciones de la cadena de reemplazo.
>>> p = re.compile('section{ (?P<name> [^}]* ) }', re.VERBOSE)
>>> p.sub(r'subsection{\1}','section{First}')
'subsection{First}'
>>> p.sub(r'subsection{\g<1>}','section{First}')
'subsection{First}'
>>> p.sub(r'subsection{\g<name>}','section{First}')
'subsection{First}'
replacement también puede ser una función, lo que le brinda aún más control. Si replacement es una función, la función se llama para cada ocurrencia no superpuesta de pattern. En cada llamada, a la función se le pasa un argumento match object para la coincidencia y puede usar esta información para calcular la cadena de reemplazo deseada y retornarla.
En el siguiente ejemplo, la función de reemplazo traduce decimales a hexadecimales:
>>> def hexrepl(match):
... "Return the hex string for a decimal number"
... value = int(match.group())
... return hex(value)
...
>>> p = re.compile(r'\d+')
>>> p.sub(hexrepl, 'Call 65490 for printing, 49152 for user code.')
'Call 0xffd2 for printing, 0xc000 for user code.'
Cuando se usa la función module-level re.sub()
, el patrón se pasa como primer argumento. El patrón puede proporcionarse como un objeto o como una cuerda; Si necesita especificar marcas de expresión regular, debe usar un objeto de patrón como primer parámetro o usar modificadores incrustados en la cadena de patrón, por ejemplo sub("(?i)b+", "x", "bbbb BBBB")
retorna 'x x'
.
Problemas comunes¶
Las expresiones regulares son una herramienta poderosa para algunas aplicaciones, pero de alguna manera su comportamiento no es intuitivo y, a veces, no se comportan de la forma esperada. Esta sección señalará algunos de los errores más comunes.
Uso de métodos de cadenas de caracteres¶
A veces, usar el módulo re
es un error. Si está haciendo coincidir una cadena de caracteres fija, o una clase de un solo caracter, y no está usando ninguna característica re
como la marca IGNORECASE
, entonces todo el poder de las expresiones regulares puede que no sea necesario. Las cadenas tienen varios métodos para realizar operaciones con cadenas fijas y, por lo general, son mucho más rápidas, porque la implementación es un único bucle C pequeño que se ha optimizado para este propósito, en lugar del motor de expresión regular más grande y generalizado.
Un ejemplo podría ser reemplazar una sola cadena fija por otra; por ejemplo, puede reemplazar word
por deed
. re.sub()
parece la función a utilizar para esto, pero considere el método replace()
. Tenga en cuenta que replace()
también reemplazará word
dentro de las palabras, convirtiendo swordfish
en sdeedfish
, pero la RE naíf word
también lo habría hecho. (Para evitar realizar la sustitución en partes de palabras, el patrón tendría que ser \bword\b
, para requerir que word
tenga un límite de palabra en cada lado. Esto lleva el trabajo más allá de las habilidades de replace()
.)
Otra tarea común es eliminar cada aparición de un solo carácter de una cadena o reemplazarlo con otro solo carácter. Puede hacer esto con algo como re.sub('\n', ' ', S)
, pero translate()
es capaz de realizar ambas tareas y será más rápido que cualquier expresión regular la operación puede ser.
En resumen, antes de pasar al módulo re
, considere si su problema puede resolverse con un método de cadena de caracteres más rápido y simple.
match() versus search()¶
La función match()
solo verifica si la RE coincide con el comienzo de la cadena de caracteres, mientras que search()
buscará una coincidencia en la cadena de caracteres. Es importante tener en cuenta esta distinción. Recuerde match()
solo informará una coincidencia exitosa que comenzará en 0; si la coincidencia no comienza en cero, match()
no lo informará.
>>> print(re.match('super', 'superstition').span())
(0, 5)
>>> print(re.match('super', 'insuperable'))
None
Por otro lado, search()
escaneará hacia adelante a través de la cadena de caracteres, informando la primera coincidencia que encuentre.
>>> print(re.search('super', 'superstition').span())
(0, 5)
>>> print(re.search('super', 'insuperable').span())
(2, 7)
A veces, tendrá la tentación de seguir usando re.match()
, y simplemente agregar .*
al frente de su RE. Resista esta tentación y use re.search()
en su lugar. El compilador de expresiones regulares realiza un análisis de las RE para acelerar el proceso de búsqueda de coincidencias. Uno de esos análisis determina cuál debe ser el primer carácter de una coincidencia; por ejemplo, un patrón que comienza con Crow
debe coincidir con una 'C'
.El análisis permite que el motor escanee rápidamente a través de la cadena en busca del carácter inicial, solo probando la coincidencia completa si se encuentra una 'C'
.
Agregar .*
anula esta optimización, lo que requiere escanear hasta el final de la cadena y luego retroceder para encontrar una coincidencia para el resto de la RE. Utilice re.search()
en su lugar.
Codiciosa versus no codiciosa (Greedy versus Non-Greedy)¶
Al repetir una expresión regular, como en a*
, la acción resultante es consumir la mayor cantidad posible del patrón. Este hecho suele molestarle cuando intenta hacer coincidir un par de delimitadores equilibrados, como los corchetes angulares que rodean una etiqueta HTML. El patrón ingenuo para hacer coincidir una sola etiqueta HTML no funciona debido a la naturaleza codiciosa de .*
.
>>> s = '<html><head><title>Title</title>'
>>> len(s)
32
>>> print(re.match('<.*>', s).span())
(0, 32)
>>> print(re.match('<.*>', s).group())
<html><head><title>Title</title>
La RE coincide con el '<'
en '<html>'
, y el .*
consume el resto de la cadena de caracteres. Sin embargo, aún queda más en la RE y el >
no puede coincidir al final de la cadena de caracteres, por lo que el motor de la expresión regular tiene que retroceder carácter por carácter hasta que encuentre una coincidencia para el >
. La coincidencia final se extiende desde el “<”` en '<html>'
al '>'
en '</title>'
, que no es lo que queremos.
En este caso, la solución es utilizar los calificadores no codiciosos *?
, +?
, ??
, o {m,n}?
, Que coinciden como poco texto como sea posible. En el ejemplo anterior, el '>'
se prueba inmediatamente después de las primeras coincidencias '<'
, y cuando falla, el motor avanza un carácter a la vez, volviendo a intentar el '>'
en cada paso. Esto produce el resultado correcto:
>>> print(re.match('<.*?>', s).group())
<html>
(Tenga en cuenta que analizar HTML o XML con expresiones regulares es doloroso. Los patrones rápidos y sucios manejarán casos comunes, pero HTML y XML tienen casos especiales que romperán la expresión regular obvia; para cuando haya escrito una expresión regular que maneja todos los casos posibles, los patrones serán muy complicados. Utilice un módulo analizador HTML o XML para tales tareas.)
Usando re.VERBOSE¶
Probablemente ya haya notado que las expresiones regulares son una notación muy compacta, pero no son muy legibles. Las RE de complejidad moderada pueden convertirse en largas colecciones de barras invertidas, paréntesis y metacarácteres, lo que dificulta su lectura y comprensión.
Para tales RE, especificar el flag re.VERBOSE
al compilar la expresión regular puede ser útil, porque le permite formatear la expresión regular con mayor claridad.
La flag re.VERBOSE
tiene varios efectos. Los espacios en blanco en la expresión regular que no están dentro de una clase de caracteres se ignoran. Esto significa que una expresión como dog | cat
es equivalente al menos legible dog | cat
, pero [a b]
seguirá coincidiendo con los caracteres 'a'
, 'b'
o un espacio. Además, también puede poner comentarios dentro de una RE; los comentarios se extienden desde un carácter #
hasta la siguiente línea nueva. Cuando se usa con cadenas entre comillas triples, esto permite que las REs sean formateados de manera más ordenada:
pat = re.compile(r"""
\s* # Skip leading whitespace
(?P<header>[^:]+) # Header name
\s* : # Whitespace, and a colon
(?P<value>.*?) # The header's value -- *? used to
# lose the following trailing whitespace
\s*$ # Trailing whitespace to end-of-line
""", re.VERBOSE)
Esto es mas legible que:
pat = re.compile(r"\s*(?P<header>[^:]+)\s*:(?P<value>.*?)\s*$")
Feedback¶
Las expresiones regulares son un tema complicado. ¿Le ayudó este documento a comprenderlas? ¿Hubo partes que no estaban claras o problemas que encontró que no se trataron aquí? Si es así, envíe sugerencias de mejora al autor.
El libro más completo sobre expresiones regulares es casi con certeza Mastering Regular Expressions de Jeffrey Friedl, publicado por O’Reilly. Desafortunadamente, se concentra exclusivamente en los tipos de expresiones regulares de Perl y Java, y no contiene ningún material de Python, por lo que no será útil como referencia para la programación en Python. (La primera edición cubría el módulo regex
de Python, ahora eliminado, que no le ayudará mucho.) Considere sacarlo de su biblioteca.