martes, 26 de abril de 2022

Expresiones Regulares

Las expresiones regulares son secuencias de caracteres que conforman un patrón de búsqueda, las cuales son formalizadas por medio de una sintaxis especifica. Estos patrones se interpretan como un conjunto de instrucciones, que luego se aplican sobre un texto de entrada para producir un subconjunto o versión modificada del texto original. Muchos problemas de procesamiento de texto se pueden resolver fácilmente con ellas.

Lo primero que tenemos que hacer es importar el modulo de expresión regular. Ejemplo básico de como se crean y utilizan expresiones regulares.
import re

cadena_re = "{{(.*?)}}"
una_cadena = "Esta es una cadena con {{palabras}} metidas\
entre {{llaves}} para ver un {{ejemplo}} de {{expresion regular}}"

for coincidencia in re.findall(cadena_re, una_cadena):
	print(f'Coincidencia: {coincidencia}')
Coincidencia: palabras
Coincidencia: llaves
Coincidencia: ejemplo
Coincidencia: expresion regular

Lo primero que hicimos fue importar el modulo re. (regular expresion). A continuación creamos una cadena con el patrón que buscamos en el ejemplo. Este patrón coincidirá con dos llaves abiertas consecutivas ({{) seguidas por cualquier texto (o no texto) seguidos por dos llaves cerradas consecutivas (}}). A continuación, creamos una cadena de texto, "una_cadena" que contiene una mezcla de grupos de palabras englobados en llaves dobles y palabras que no lo están. Por ultimo pasamos por los resultados de la función findall() del módulo re que va buscando en "una_cadena" el patrón establecido en "cadena_re"

Dos formas de trabajar con expresiones regulares en Python.

La primera es utilizar las funciones en el módulo re directamente, como en el ejemplo enterior.

La segunda es crear un objeto de expresión regular compilada y utilizar los métodos en ese objeto. Una expresión regular compilada es la que se crea al pasar un patrón a re.compile() 

Veamos un ejemplo:

import re

re_obj = re.compile("{{(.*?)}}")

una_cadena = "Esta es una cadena con {{palabras}} metidas\
entre {{llaves}} para ver un {{ejemplo}} de\
{{expresion regular}}"

for coincidencia in re_obj.findall(una_cadena):
	print(f'Coincidencia: {coincidencia}')
Coincidencia: palabras
Coincidencia: llaves
Coincidencia: ejemplo
Coincidencia: expresion regular

El uso de un método y otro es cuestión de gusto, sin embargo es preferible usar la segunda ya que es un método más eficiente y rápido especialmente cuando se busca un patrón en un archivo de gran tamaño (Por ejemplo 500.000 líneas). 

Componentes de las expresiones regulares.

- literales. Cualquier caracter que se encuentre a si mismo, a menos que se trate de un metacaracter el cual tendrá un significado especial.  Por ejemplo el patrón "hola" encontrará todas las veces que "hola" aparezca en el texto que procesemos. 

- Secuencias de escape. En la sintexis de las expresiones regulares podemos utilizar cadenas de escape (\ la barra inclinada más otro caracter) para ciertos casos particulares. (saltos de linea, tabuladores etc)

Secuencia de escapeSignificado
\nNueva línea (new line). El cursor pasa a la primera posición de la línea siguiente.
\tTabulador. El cursor pasa a la siguiente posición de tabulación.
\\Barra diagonal inversa
\vTabulación vertical.
\oooCarácter ASCII en notación octal.
\xhhCarácter ASCII en notación hexadecimal.
\xhhhhCarácter Unicode en notación hexadecimal.

- Clases de caracteres.  Se crean poniendo los caracteres a buscar entre corchetes. []  Por ejemplo si usamos el patrón r'aeiou' en un texto encuentra todas las vocales que haya en el mismo.

import re

patron = r'[aeiou]'
texto = "me gusta el abecedario"

print(re.findall(patron, texto))
['e', 'u', 'a', 'e', 'a', 'e', 'e', 'a', 'i', 'o']

Cada corchete equivale a un caracter en el texto de búsqueda. Para que quede más claro. Si ponemos, por ejemplo, dos corchetes juntos uno con el caracter a y otro con el b nos encontrará en el texto esos dos caracteres donde estén situados de forma consecutiva. 

import re

patron = r'[a][b]'
texto = "me gusta el abecedario"

print(re.findall(patron, texto))
['ab']

rangos de caracteres.

Sirven para escribir de forma más fácil un patron. Por ejemplo si quisiéramos encontrar en un texto cualquier letra minúscula tendríamos que usar algo como [abcdefghijklmnñopqrstuvwxyz]. Sin embargo usando los rangos de caracteres esto se simplifica ya que solo tenemos que escribirlo de esta forma: [a-z] en donde le estamos diciendo al ordenador que busque cualquier caracter entre la a y la z.

Nota: pero no localiza directamente caracteres locales, como puede ser la ñ en nuestro caso. Pero se puede hacer de otra forma lo vemos más adelante.

algunos ejemplos:

[a-z]   Cualquier caracter alfabético en minúsculas.

[G-P] Letras mayúsculas de la la G  a la P

[0-9] cualquier dígito

Incluso se pueden anidar:

[a-zA-Z] cualquier letra minúscula o mayúscula

[^a-z] al poner el circunflejo dentro de los corchetes significa lo contrario, es decir, en este caso que no tenga ninguna letra minuscula o lo que viene a ser lo mismo que encuentre los caracteres en mayúscula [A-Z]

Metacaracteres. 

Son caracteres especiales que son la esencia de las expresiones regulares.  Dentro de ellos podemos encontrar:

- metacaracteres - delimitadores o también llamados anclas.

Nos permiten especificar en que posición de la cadena debe encontrarse el elemento buscado:

Metacaracter	Descripción
^	        Permite buscar al inicio de una cadena o línea (opción -MULTILINE).
$	        Permite buscar al final de una cadena o linea (opción - MULTILINE).
\A	        Permite buscar al inicio de un texto.
\Z	        Permite busca al final del texto.
.	        cualquier carácter en la línea.
\b	        Permite buscar una cadena al principio o final de una palabra o como 
                coincidencia completa de la palabra.
\B	        Permite buscar una coincidencia completa en una cadena que no se encuentra
                en el límite de una palabra.

- metacaracteres - clases predefinidas.

MetacaracterDescripción
\wun caracter alfanumérico (incluye "_").
\Wun caracter no alfanumérico.
\dun caracter numérico, un dígito.
\Dun caracter no numérico.
\scualquier espacio (lo mismo que [ \t\n\r\f]).
\Sun no espacio.

- metacaracteres - iteradores o cuantificadores. Cualquier elemento de una expresión regular puede utilizarse seguido este tipo de metacarter. Usando los mismo podemos definir el número de ocurrencias de caracter previo o dicho de otra forma multiplican el patrón que les precede.

Metacaracter	Descripción
*	        cero o más repeticiones de la cosa previa ya sea un caracter, un rango de
                caracteres [], o un grupo de caracteres entre paréntesis, similar a {0,}.
+	        una o más. Igual que el anterior pero aqui con una o más repeticiones
                , similar a {1,}.
?	        Lo mismo que el anterior pero con cero o una repeticiones, similar a {0,1}.
{n}	        exactamente n veces.
{n,}	        por lo menos n veces.
{n,m}	        por lo menos n pero no más de m veces.
*?	        cero o más, similar a {0,}?.
+?	        una o más, similar a {1,}?.
??	        cero o una, similar a {0,1}?.
{n}?	        exactamente n veces.
{n,}?	        por lo menos n veces.
{n,m}?	        por lo menos n pero no más de m veces.


- metacaracteres - alternativas. Se usa para ello el caracter | que es un equivalente a un or. Por ejemplo en el texto 'Pedro monta un mueble en el monte', el patrón r'mont[a|e]'  nos devolveriá tanto 'monta' como 'monte'. O por ejemplo el patron pre(fijo|facio) encontrara las palabras prefijo o prefacio.

Otros metacaracteres:

- metacaracteres- subexpresiones. Definen subexpresiones dentro de una expresión regular. 

-metacaracteres - memorias.

Puedes encontrar una guia rápida sobre lenguaje de expresiones regulares en el siguiente enlace de Python.


Las cadenas raw, aquellas en las que los caracteres precedidos por una barra invertida no se sustituyen por su contrapartida, se pueden utilizar para denotar cadenas que no interpretan secuencias de escape, lo cual es muy útil cuando utilizamos expresiones regulares. Veámoslo como un ejemplo:

import re

patron_raw = r'\b[a-z]+\b'
sin_patron_raw = '\b[a-z]+\b'

una_cadena = "unas pocas palabras"

print(re.findall(patron_raw, una_cadena))

print(re.findall(sin_patron_raw, una_cadena))
['unas', 'pocas', 'palabras']
[]

Como hemos visto la expresión regular \b nos delimita una palabra. Por lo tanto tanto lo que estamos buscando son palabras individuales en minúsculas. Como vemos en el ejemplo, en donde hemos usado patron_raw los resultados nos muestran correctamente las palabras del texto ya que interpreto \b como dos caracteres son un significado especial y no como retrocesos, sin embargo en sin_patron_raw no nos sale nada en absoluto ya que se interpretó los caracteres \b como una secuencia de escape que representaba el caracter de retroceso.

Los cuatro principales métodos  de expresión regular que más se utilizan son:

1.- re.findall(patron, cadena) = Genera una lista con todas las ocurrencias del patrón.

2.- re.finditer(patron, cadena) = Similar al findall pero en vez de generar una lista nos genera un objeto.

3.- re.match(patron, cadena) = Busca al principio de la cadena hay una coincidencia con el patron especificado y devuelve un objeto. Si no encuentra nada devuelve None.

4.- re.search(patron, cadena) = Busca en la cadena la primera coincidencia con el patrón especificado.

Otros métodos son:

- re.split(patron, cadena, maxsplit) = Divide la cadena utilizando el patron especificado y devuelve una lista con las particiones. Por defecto maxsplit=0 implica realizar todas las particiones posibles.

- re.sub(patron, cadena_de_remplazo, cadena de texto, count=0) = Sustituye por defecto todos los patrones por la cadena de remplazo, salvo que se indique que count sea diferente de cero.

- re.subn(patron, cadena_de_remplazo, cadena de texto, count=0) = lo mismo que el anterior pero devuelve también el número de reemplazos realizados.



No hay comentarios:

Publicar un comentario