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

10 alternativas a Cuevana para ver películas online

10 alternativas a Cuevana para ver películas online : Durante este último tiempo, en Cuevana se sucedieron varios “problemas” por los cuales hubo que ajustar algunas cosas antes de tiempo (como el rediseño del sitio), que dejaron a algunos usuarios ciertos problemas para acceder a las películas o series del portal. Pero realmente esto es algo que no incumbe a los usuarios y, como sabemos, existen muchas otras alternativas a Cuevana dando vueltas por Internet, que intentaremos presentar aquí mismo. Los sitios que repasaremos funcionan del mismo modo que Cuevana, mediante la instalación de un plugin que permite visualizar los videos de Megaupload o WUShare, entre otros servicios, en una calidad de imágen realmente excelente. Tal como sucede con el más popular servicio, todos ellos tienen publicidad que en algunos casos resulta insoportable, pero como dice Federico en DotPod “a caballo regalado no se le miran los dientes”. Alternativas a Cuevana 1. Moviezet Posiblemente el mejor clon d

Sitio alternativo a Cuevana: Moviezet

Sitio alternativo a Cuevana: Moviezet : Nadie se quiere enfrentar al monstruo Cuevana , tan popular por estos días que es casi imposible ver tu serie favorita o tu película sin tener problema de saturación de tráfico. Pero hay proyectos muy sanos y prometedores, sobre todo porque están basados como una muy buena alternativa . Señores estamos hablando obviamente de un sitio alternativo a Cuevana, llamado Moviezet. Como bien dijimos, Moviezet es una excelente alternativa a Cuevana, ya que podremos ver películas y series de forma gratuita sin necesidad de que existan cortes – al mejor estilo Megavideo – y que podremos tener un seguimiento, es decir, si miramos una serie, podremos ver toda la lista con los capítulos disponibles. Lo que tiene de novedoso este sitio web Moviezet , es que tiene películas y series que quizá en Cuevana no se puedan conseguir, pero atención, que puede suceder lo mismo, pero al revés. Entonces aquí intervenimos nosotros y te daremos un sabio consejo, para no

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