Passa al contingut principal

Guía Python: Expresiones Regulares

Guía Python: Expresiones Regulares:
Cuando manejamos texto, sin duda una de las operaciones más comunes es la búsqueda de una subcadena, ya sea para obtener su posición en el texto o simplemente para comprobar si está presente. Si la cadena que buscamos es fija, son suficientes los métodos como find(), index() o similares, pero éstos no ayudan si lo que se busca es una subcadena con cierta forma.
Al buscar direcciones de correo electrónico, números de teléfono, validar campos de entrada, o encontrar por ejemplo una letra mayúscula seguida de dos minúsculas y de 5 dígitos entre 1 y 3, es necesario recurrir a las Expresiones Regulares, también conocidas como Patrones.

Patrones

Las expresiones regulares son un lenguaje potente de descripción de texto, y no creo que exista un lenguaje moderno que no permita usarlas. Las reglas con las que se forman son bastante simples, pero requiere práctica aprender a combinarlas correctamente.
Con expresiones regulares podemos buscar una subcadena al principio o al final del texto, si queremos que se repita cierta cantidad de veces, si queremos que algo NO aparezca, o si debe aparecer una subcadena entre varias posibles. Permite además capturar aquellos trozos del texto que coincidan con la expresión, para guardarlos en una variable o reemplazarlos por una cadena predeterminada (o incluso una cadena formada por los mismos trozos capturados). Veremos algunos aspectos básicos de las expresiones regulares, sin entrar en detalles.

Metacaracteres

Se conoce como metacaracteres a aquellos caracteres que, dependiendo del contexto, tienen un significado especial para las expresiones regulares, y que por lo tanto debemos escapar (colocándoles una contrabarra \ delante) si queremos buscarlos explícitamente. A continuación veremos los más importantes:
  • Anclas: Indican que lo que queremos encontrar se encuentra al principio o al final de la cadena. Combinándolas, podemos buscar algo que represente a la cadena entera:
    ^patron: coincide con cualquier cadena que comience con patron.
    patron$
    : coincide con cualquier cadena que termine con patron.

    ^patron$: coincide con la cadena exacta patron.
  • Clases de caracteres: Se utilizan cuando se quiere buscar un caracter dentro de varias posibles opciones. Una clase se delimita entre corchetes (paréntesis rectos) y lista posibles opciones para el caracter que representa:
    [abc]: coincide con a, b, o c
    [387ab]: coincide con 3, 8, a o b
    niñ[oa]s: coincide con niños o niñas.
    Para evitar errores, en caso de que queramos crear una clase de caracteres que contenga un corchete, debemos escribir una barra \ delante, para que el motor de expresiones regulares lo considere un caracter normal: la clase [ab\[] coincide con a, b y [.

Rangos

Si queremos encontrar un número, podemos usar una clase como [0123456789], o podemos utilizar un rango. Un rango es una clase de caracteres abreviada que se crea escribiendo el primer caracter del rango, un guión, y el último caracter del rango. Múltiples rangos pueden definirse en la misma clase de caracteres.
  • [a-c]: equivale a [abc]
  • [0-9]: equivale a [0123456789]
  • [a-d5-8]: equivale a [abcd578]
Es importante notar que si se quiere buscar un guión, debe colocarse al principio o al final de la clase (inmediatamente después del corchete izquierdo o inmediatamente antes del corchete derecho) o escaparse. Si no se hace de esta forma, el motor de expresiones regulares intentará crear un rango y la expresión no funcionará como debe (o dará un error). Si queremos, por ejemplo, crear una clase que coincida con los caracteres a, 4 y -, debemos escribirla como:
  • [a4-]
  • [-a4]
  • [a\-4]

Rango negado

Así como podemos listar los caracteres posibles en cierta posición de la cadena, también podemos listar caracteres que NO deben aparecer. Para lograrlo, debemos negar la clase, colocando un circunflejo inmediatamente después del corchete izquierdo:
  • [^abc]: coincide con cualquier caracter distinto a a, b y c

Clases predefinidas

Hay algunas clases que se usan frecuentemente y por eso existen formas abreviadas para ellas. En Python (así como en otros lenguajes) se soportan las clases predefinidas de Perl y de POSIX (si no sabes lo que eso quiere decir, quizás quieras leer en Wikipedia su signficado). Algunas de estas clases son:
  • \d (POSIX [[:digit:]]): equivale a [0-9]
  • \s (POSIX [[:space:]]): caracteres de espacio en blanco (espacio, tabulador, nueva línea, etc)
  • \w (POSIX [[:word:]]): letras minúsculas, mayúsculas, números e infraguión (_)
Además de las listadas arriba (y el resto, no listadas) existe una clase de caracteres que coincide con cualquier caracter (sea letra, número, o un caracter especial). Esta clase es el punto:
  • "." : coincide con cualquier caracter.

Cuantificadores

Son caracteres que multiplican el patrón que les precede. Mientras que con las clases de caracteres podemos buscar un dígito, o una letra, con los cuantificadores podemos buscar cero o más letras, al menos 7 dígitos, o entre tres y cinco letras mayúsculas.
Los cuantificadores son:
  • ?: coincide con cero o una ocurrencia del patrón (dicho de otra forma: hace que el patrón sea opcional)
  • +: coincide con una o más ocurrencias del patrón
  • *: coincide con cero o más ocurrencias del patrón.
  • {x}: coincide con exactamente x ocurrencias del patrón
  • {x, y}: coincide con al menos x y no más de y ocurrencias. Si se omite x, el mínimo es cero, y si se omite y, no hay máximo. Esto permite especificar a los otros como casos particulares: ? es {0,1}, + es {1,} y * es {,} o {0,}.
Ejemplos:
.* : cualquier cadena, de cualquier largo (incluyendo una cadena vacía)
[a-z]{3,6}: entre 3 y 6 letras minúsculas
\d{4,}: al menos 4 dígitos
.*hola!?: una cadena cualquiera, seguida de hola, y terminando (o no) con un !

Otros metacaracteres

Existen otros metacaracteres en el lenguaje de las expresiones regulares:
  • ?: Además de servir como cuantificador, puede modificar el comportamiento de otro. De forma predeterminada, un cuantificador coincide con la mayor cadena posible; cuando se le coloca un ?, se indica que se debe coincidir con la menor cadena posible. Esto es: dada la cadena bbbbb, b+ coincide con la cadena entera, mientras que b+? coincide solamente con b (la menor cadena que cumple el patrón).
  • (): agrupan patrones. Sirven para que aquel trozo de la cadena que coincida con el patrón sea capturado (veremos luego cómo usar el valor capturado), o para delimitar el alcance de un cuantificador. Ejemplo: ab+ coincide con ab, abb, abbbbb, ..., mientras que (ab)+ coincide con ab, abab, abab...
  • | : permite definir opciones para el patrón: perro|gato coincide con perro y con gato.

Módulo re

Para utilizar Expresiones Regulares, Python provee el módulo re. Importando este módulo podemos crear objetos de tipo patrón y generar objetos tipo matcher, que son los que contienen la información de la coincidencia del patrón en la cadena.
Creando un patrón
Para crear un objeto patrón, importamos el módulo re y utilizamos la función compile:
import os
patron = re.compile('a[3-5]+') # coincide con una letra, seguida de al menos 1 dígito entre 3 y 5
A partir de ahora, podemos usar el objeto patron para comparar cadenas con la expresión regular.

Buscar el patrón en la cadena

Para buscar un patrón en una cadena, Python provee los métodos search y match. La diferencia entre ambos es que, mientras search busca en la cadena alguna ocurrencia del patrón, match devuelve None si la ocurrencia no se da al principio de la cadena:
>>> cadena = 'a44453'
>>> patron.match(cadena)
<_sre.SRE_Match object at 0x02303BF0>
>>> patron.search(cadena)
<_sre.SRE_Match object at 0x02303C28>

>>> cadena = 'ba3455' # la coincidencia no está al principio!
>>> patron.search(cadena)
<_sre.SRE_Match object at 0x02303BF0>
>>> print patron.match(cadena)
None
Si sabemos que obtendremos más de una coincidencia, podemos usar el método findall, que recorre la cadena y devuelve una lista de coincidencias:
>>> patron.findall('a455 a333b435')
['a455', 'a333']
O el método finditer, que devuelve un iterador que podemos usar en el bucle for:
>>> for m in patron.finditer('a455 a333b435'):  # cada m es un objeto tipo matcher
...   print m.groups()
...
('a', '455')
('a', '333')
('b', '435')

Objetos matcher

Más arriba se mencionó el uso del los paréntesis en un patrón. Cuando se obtiene una coincidencia del patrón en una cadena, cada grupo delimitado por paréntesis captura el texto que haya coincidido con él. Estos grupos son accesibles a través de un objeto tipo matcher devuelto por search o match. Los grupos se numeran de izquierda a derecha según su orden de aparición en el patrón, y podemos usar este número para acceder al contenido del grupo con el método group del objeto matcher.
De forma alternativa, podemos usar el método groups que devuelve una lista de grupos.
>>> patron = re.compile('([ab])([3-5]+)')  # ahora la letra se capturará en el grupo 1, y los números en el 2
>>> matcher = patron.search('a455 a333b435')
>>> matcher.group(0)  # el grupo 0 es el trozo de cadena que coincidió con el patrón completo
'a455'
>>> matcher.group(1)
'a'
>>> matcher.group(2)
'455'
>>> matcher.groups()  # groups() no incluye el grupo 0
('a', '455')
Los objetos matcher guardan más información sobre la coincidencia, por ejemplo la posición de la cadena en la que se produjo (en este caso, al principio de la cadena):
>>> matcher.pos
0
También permiten sustituir los grupos capturados en una cadena cualquiera, mediante el uso de referencias de la forma \g<x>, donde x es el número de grupo:
>>> print matcher.expand('La cadena que coincidió fue \g<0>, el grupo 1 es \g<1> y el grupo 2 es \g<2>')
La cadena que coincidió fue a455, el grupo 1 es a y el grupo 2 es 455
Vale notar que, si bien findall no devuelve objetos tipo matcher, sí proporciona los grupos de forma similar, como una lista de tuplas:
>>> patron.findall('a455 a333b435')
[('a', '455'), ('a', '333'), ('b', '435')]

Reemplazo de cadenas

Similar a la combinación search + expand, existe el método sub, cuya función es encontrar todas las coincidencias de un patrón y sustituirlas por una cadena. El método recibe dos parámetros: el primero es la cadena con la que se sustituirá el patrón y el segundo es la cadena sobre la que queremos aplicar la sustitución.
Se pueden utilizar referencias de la misma forma que antes:
>>> patron.sub("X", 'a455 a333b435')  # sustituye todas las ocurrencias por X
'X XX'
>>> patron.sub("LETRA(\g<1>), NUMERO(\g<2>)", 'a455 a333b435')  # El reemplazo depende de lo que se capture
'LETRA(a), NUMERO(455) LETRA(a), NUMERO(333)LETRA(b), NUMERO(435)'

Grupos con nombre

De la misma forma en la que podemos usar grupos numerados, también podemos usar grupos con nombre. Esto hace más cómodo el manejo de patrones complejos, ya que siempre es más natural manejar un nombre que un número. Además, si solamente usamos números de grupo, podemos tener errores si luego modificamos el patrón para agregar algún grupo: al agregarlo bien podríamos estar cambiando el índice de otro posterior.
Los nombres de grupo se definen agregando ?P<nombre_de_grupo> al paréntesis de apertura del grupo.
>>> patron = re.compile('(?P<letra>[ab])(?P<numero>[3-5]+)')  # defino dos grupos con nombre 'letra' y 'numero'
>>> matcher = patron.search('a455 a333b435')  # busco en la misma cadena de antes
>>> matcher.groups()    # groups y group(n) funcionan igual
('a', '455')
>>> matcher.group(1)
'a'
>>> matcher.group('letra')   # pero además ahora puedo acceder por nombre
'a'
>>> matcher.group('numero')
'455'
>>> matcher.expand('La letra es \g<letra>')  # las referencias se usan con el nombre en vez de con el número
'La letra es a'
Otra ventaja de utilizar nombres de grupo, es que podemos usar el método groupdict para obtener un diccionario de pares nombre-contenido de cada grupo:
>>> matcher.groupdict()
{'letra': 'a', 'numero': '455'}

Modificadores para el patrón

Existen varios modificadores que podemos pasar al método compile para modificar el comportamiento del patrón. Los más usados son:
  • re.I o re.IGNORECASE: hace que el patrón no distinga entre minúsculas y mayúsculas.
  • re.M o re.MULTILINE: modifica el comportamiento de ^ y $ para que coincidan con el comienzo y final de cada línea de la cadena, en vez de coincidir con el comienzo y final de la cadena entera
  • re.S o re.DOTALL: hace que el punto (.) coincida además con un salto de línea (sin este modificador, el punto coincide con cualquier caracter excepto un salto de línea)
Cada modificador se usa como segundo parámetro de la función, podemos unir los efectos de más de un modificador separándolos con |. Por ejemplo:
>>> patron = re.compile('el patron', re.I | re.MULTILINE)
La lista completa está en 7.2. re — Regular expression operations.
La próxima semana trabajaremos con Diccionarios, funciones y archivos en Python.

Alvaro Martinez Alvaro Martinez para Maestros del Web.
Agrega tu comentario | Enlace permanente al artículo

Síguenos en: @maestros | Fan page

Comentaris

Entrades populars d'aquest blog

Learn Composition from the Photography of Henri Cartier-Bresson

“Do you see it?” This question is a photographic mantra. Myron Barnstone , my mentor, repeats this question every day with the hopes that we do “see it.” This obvious question reminds me that even though I have seen Cartier-Bresson’s prints and read his books, there are major parts of his work which remain hidden from public view. Beneath the surface of perfectly timed snap shots is a design sensibility that is rarely challenged by contemporary photographers. Henri Cartier-Bresson. © Martine Franck Words To Know 1:1.5 Ratio: The 35mm negative measures 36mm x 24mm. Mathematically it can be reduced to a 3:2 ratio. Reduced even further it will be referred to as the 1:1.5 Ratio or the 1.5 Rectangle. Eyes: The frame of an image is created by two vertical lines and two horizontal lines. The intersection of these lines is called an eye. The four corners of a negative can be called the “eyes.” This is extremely important because the diagonals connecting these lines will form the breakdown ...

El meu editor de codi preferit el 2024, que això ja se sap que va canviant 😄

Visual Code Visual Code és un editor de codi font lleuger, però potent que s’executa al teu escriptori i està disponible per a Windows, macOS i Linux. Compta amb suport integrat per a JavaScript, TypeScript i Node.js i té un ric ecosistema d’extensions per a altres llenguatges i entorns d’execució (com C++, C#, Java, Python, PHP, Go, .NET).  És una eina ideal per a desenvolupar i depurar aplicacions web i en el núvol. Per què Visual Code? Visual Code té molts avantatges com a editor de codi font, com per exemple: És gratuït, ràpid i fàcil d’instal·lar i actualitzar. Té un ampli ecosistema d’extensions que et permeten afegir funcionalitats i personalitzar la teva experiència de desenvolupament. Té un suport integrat per a molts llenguatges i entorns d’execució, i et permet depurar i executar el teu codi des del mateix editor. Té una interfície senzilla i elegant, amb diferents temes i modes de visualització. Té un sistema de sincronització de configuracions que et permet guardar les...

Las Mejores Aplicaciones Gratis para iPad de 2012

Las Mejores Aplicaciones Gratis para iPad de 2012 : ¿No tienes ni un duro? No te preocupes, pues hoy os traemos una extensa selección de las mejores apps gratuitas que puedes conseguir en la App Store para que llenes tu iPad de calidad, sin gastar nada de nada.   ¿Estás buscando juegos o apps gratis para tu iPad? En la App Store hay más de 500,000 apps y juegos, y una gran cantidad de ellos está disponible de forma totalmente gratuita. Aquí vamos con la selección de las mejores Apps gratis para iPad (todos los modelos), organizada por categoría. ¿Estás preparado? Las Mejores Apps Gratis de Redes Sociales para iPad Nombre Facebook Gratis Categoría Redes sociales Facebook es la red social más famosa del mundo , con casi mil millones de usuarios. Su app para iPad ha tardado, pero aquí está. Nombre Twitter Gratis Categoría Redes sociales Twitter es la red de microblogging por excelencia. La forma más rápida y directa de informar y mantenerse informado de las cosa...