<SYSTEM_TASK:>
Given the following text description, write Python code to implement the functionality described below step by step
<END_TASK>
<USER_TASK:>
Description:
 Expresiones Regulares con Python
Esta notebook fue creada originalmente como un blog post por Raúl E. López Briega en Mi blog sobre Python. El contenido esta bajo la licencia BSD.
<img alt="Expresiones regulares" title="Expresiones regulares" src="https
Step1:  Buscando coincidencias
Una vez que hemos importado el módulo, podemos empezar a tratar de buscar coincidencias con un determinado patrón de búsqueda. Para hacer esto, primero debemos compilar nuestra expresion regular en un objeto de patrones de Python, el cual posee métodos para diversas operaciones, tales como la búsqueda de coincidencias de patrones o realizar sustituciones de texto.
Step3:  Ahora que ya tenemos el objeto de expresión regular compilado podemos utilizar alguno de los siguientes métodos para buscar coincidencias con nuestro texto.
match()
Step4:  Como podemos ver en estos ejemplos, cuando hay coincidencias, Python nos devuelve un Objeto de coincidencia (salvo por el método findall() que devuelve una lista). Este Objeto de coincidencia también tiene sus propios métodos que nos proporcionan información adicional sobre la coincidencia; éstos métodos son
Step6:  Modificando el texto de entrada
Además de buscar coincidencias de nuestro patrón de búsqueda en un texto, podemos utilizar ese mismo patrón para realizar modificaciones al texto de entrada. Para estos casos podemos utilizar los siguientes métodos
Step7:  Funciones no compiladas
En estos últimos ejemplos, pudimos ver casos donde utilizamos las funciones al nivel del módulo split() y subn(). Para cada uno de los ejemplos que vimos (match, search, findall, finditer, split, sub y subn) existe una versión al nivel del módulo que se puede utilizar sin necesidad de compilar primero el patrón de búsqueda; simplemente le pasamos como primer argumento la expresion regular y el resultado será el mismo. La ventaja que tiene la versión compila sobre las funciones no compiladas es que si vamos a utilizar la expresion regular dentro de un bucle nos vamos a ahorrar varias llamadas de funciones y por lo tanto mejorar la performance de nuestro programa.
Step10:  Banderas de compilación
Las banderas de compilación permiten modificar algunos aspectos de cómo funcionan las expresiones regulares. Todas ellas están disponibles en el módulo re bajo dos nombres, un nombre largo como IGNORECASE y una forma abreviada de una sola letra como I. Múltiples banderas pueden ser especificadas utilizando el operador "|" OR; Por ejemplo, re.I | RE.M establece las banderas de E y M. 
Algunas de las banderas de compilación que podemos encontrar son
Step11:  Como podemos ver en este último ejemplo, la opción VERBOSE puede ser muy util para que cualquier persona que lea nuestra expresion regular pueda entenderla más fácilmente.
Nombrando los grupos
Otra de las funciones interesantes que nos ofrece el módulo re de Python; es la posibilidad de ponerle nombres a los grupos de nuestras expresiones regulares. Así por ejemplo, en lugar de acceder a los grupos por sus índices, como en este caso...
Step12:  Podemos utilizar la sintaxis especial (?P&lt;nombre&gt;patron) que nos ofrece Python para nombrar estos grupos y que sea más fácil identificarlos.
Step13:  Otros ejemplos de expresiones regulares
Por último, para ir cerrando esta introducción a las expresiones regulares, les dejo algunos ejemplos de las expresiones regulares más utilizadas.
Validando mails
Para validar que un mail tenga la estructura correcta, podemos utilizar la siguiente expresion regular
Step14:  Validando una dirección IP
Para validar que una dirección IP tenga una estructura correcta, podemos utilizar esta expresión regular
Step15:  Validando una fecha
Para validar que una fecha tenga una estructura dd/mm/yyyy, podemos utilizar esta expresión regular
<ASSISTANT_TASK:>
Python Code:
# importando el modulo de regex de python
import re  
Explanation: Expresiones Regulares con Python
Esta notebook fue creada originalmente como un blog post por Raúl E. López Briega en Mi blog sobre Python. El contenido esta bajo la licencia BSD.
<img alt="Expresiones regulares" title="Expresiones regulares" src="https://relopezbriega.github.io/images/regex.png" width="700">
Introducción
Uno de los problemas más comunes con que nos solemos encontrar al desarrollar cualquier programa informático, es el de procesamiento de texto. Esta tarea puede resultar bastante trivial para el cerebro humano, ya que nosotros podemos detectar con facilidad que es un número y que una letra, o cuales son palabras que cumplen con un determinado patrón y cuales no; pero estas mismas tareas no son tan fáciles para una computadora. Es por esto, que el procesamiento de texto siempre ha sido uno de los temas más relevantes en las ciencias de la computación. Luego de varias décadas de investigación se logró desarrollar un poderoso y versátil lenguaje que cualquier computadora puede utilizar para reconocer patrones de texto; este lenguale es lo que hoy en día se conoce con el nombre de expresiones regulares; las operaciones de validación, búsqueda, extracción y sustitución de texto ahora son tareas mucho más sencillas para las computadoras gracias a las expresiones regulares.
¿Qué son las Expresiones Regulares?
Las expresiones regulares, a menudo llamada también regex, son unas secuencias de caracteres que forma un patrón de búsqueda, las cuales son formalizadas por medio de una sintaxis específica. Los patrones se interpretan como un conjunto de instrucciones, que luego se ejecutan sobre un texto de entrada para producir un subconjunto o una versión modificada del texto original. Las expresiones regulares pueden incluir patrones de coincidencia literal, de repetición, de composición, de ramificación, y otras sofisticadas reglas de reconocimiento de texto . Las expresiones regulares deberían formar parte del arsenal de cualquier buen programador ya que un gran número de problemas de procesamiento de texto pueden ser fácilmente resueltos con ellas.
Componentes de las Expresiones Regulares
Las expresiones regulares son un mini lenguaje en sí mismo, por lo que para poder utilizarlas eficientemente primero debemos entender los componentes de su sintaxis; ellos son:
Literales: Cualquier caracter se encuentra a sí mismo, a menos que se trate de un metacaracter con significado especial. Una serie de caracteres encuentra esa misma serie en el texto de entrada, por lo tanto la plantilla "raul" encontrará todas las apariciones de "raul" en el texto que procesamos.
Secuencias de escape: La sintaxis de las expresiones regulares nos permite utilizar las secuencias de escape que ya conocemos de otros lenguajes de programación para esos casos especiales como ser finales de línea, tabs, barras diagonales, etc. Las principales secuencias de escape que podemos encontrar, son:
Secuencia de escape | Significado
---|---
\n | Nueva línea (new line). El cursor pasa a la primera posición de la línea siguiente.
\t | Tabulador. El cursor pasa a la siguiente posición de tabulación. 
\\ | Barra diagonal inversa
\v | Tabulación vertical.
\ooo | Carácter ASCII en notación octal.
\xhh | Carácter ASCII en notación hexadecimal.
\xhhhh | Carácter Unicode en notación hexadecimal. 
Clases de caracteres: Se pueden especificar clases de caracteres encerrando una lista de caracteres entre corchetes [], la que que encontrará uno cualquiera de los caracteres de la lista. Si el primer símbolo después del "[" es "^", la clase encuentra cualquier caracter que no está en la lista.
Metacaracteres: Los metacaracteres son caracteres especiales que son la esencia de las expresiones regulares. Como son sumamente importantes para entender la sintaxis de las expresiones regulares y existen diferentes tipos, voy a dedicar una sección a explicarlos un poco más en detalle.
Metacaracteres
Metacaracteres - delimitadores
Esta clase de metacaracteres nos permite delimitar dónde queremos buscar los patrones de búsqueda. Ellos son:
Metacaracter | Descripción
---|---
^ | inicio de línea. 
$ | fin de línea. 
\A | inicio de texto. 
\Z | fin de texto. 
. | cualquier caracter en la línea. 
\b | encuentra límite de palabra. 
\B | encuentra distinto a límite de palabra.
Metacaracteres - clases predefinidas
Estas son clases predefinidas que nos facilitan la utilización de las expresiones regulares. Ellos son:
Metacaracter | Descripción
---|---
\w |    un caracter alfanumérico (incluye "_"). 
\W |    un caracter no alfanumérico. 
\d |    un caracter numérico. 
\D |    un caracter no numérico. 
\s |    cualquier espacio (lo mismo que [ \t\n\r\f]).
\S |    un no espacio.
Metacaracteres - iteradores
Cualquier elemento de una expresion regular puede ser seguido por otro tipo de metacaracteres, los iteradores. Usando estos metacaracteres se puede especificar el número de ocurrencias del caracter previo, de un metacaracter o de una subexpresión. Ellos son:
Metacaracter | Descripción
---|---
*  |    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.
*?  |   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.
En estos metacaracteres, los dígitos entre llaves de la forma {n,m}, especifican el mínimo número de ocurrencias en n y el máximo en m. 
Metacaracteres - alternativas
Se puede especificar una serie de alternativas para una plantilla usando "|" para separarlas, entonces do|re|mi encontrará cualquier "do", "re", o "mi" en el texto de entrada.Las alternativas son evaluadas de izquierda a derecha, por lo tanto la primera alternativa que coincide plenamente con la expresión analizada es la que se selecciona. Por ejemplo: si se buscan foo|foot en "barefoot'', sólo la parte "foo" da resultado positivo, porque es la primera alternativa probada, y porque tiene éxito en la búsqueda de la cadena analizada. 
Ejemplo: 
foo(bar|foo) --> encuentra las cadenas 'foobar' o 'foofoo'. 
Metacaracteres - subexpresiones
La construcción ( ... ) también puede ser empleada para definir subexpresiones de expresiones regulares. 
Ejemplos: 
(foobar){10} --> encuentra cadenas que contienen 8, 9 o 10 instancias de 'foobar' 
foob([0-9]|a+)r --> encuentra 'foob0r', 'foob1r' , 'foobar', 'foobaar', 'foobaar' etc. 
Metacaracteres - memorias (backreferences)
Los metacaracteres \1 a \9 son interpretados como memorias. \<n> encuentra la subexpresión previamente encontrada #<n>. 
Ejemplos: 
(.)\1+  --> encuentra 'aaaa' y 'cc'. 
(.+)\1+  --> también encuentra 'abab' y '123123'
(['"]?)(\d+)\1 --> encuentra '"13" (entre comillas dobles), o '4' (entre comillas simples) o 77 (sin comillas) etc. 
Expresiones Regulares con Python
Luego de esta introducción, llegó el tiempo de empezar a jugar con las expresiones regulares y Python.
Como no podría ser de otra forma tratandose de Python y su filosofía de todas las baterías incluídas; en la librería estandar de Python podemos encontrar el módulo re, el cual nos proporciona todas las operaciones necesarias para trabajar con las expresiones regulares. 
Por lo tanto, en primer lugar lo que debemos hacer es importar el modulo re.
End of explanation
# compilando la regex
patron = re.compile(r'\bfoo\b')  # busca la palabra foo
Explanation: Buscando coincidencias
Una vez que hemos importado el módulo, podemos empezar a tratar de buscar coincidencias con un determinado patrón de búsqueda. Para hacer esto, primero debemos compilar nuestra expresion regular en un objeto de patrones de Python, el cual posee métodos para diversas operaciones, tales como la búsqueda de coincidencias de patrones o realizar sustituciones de texto.
End of explanation
# texto de entrada
texto =  bar foo bar
foo barbarfoo
foofoo foo bar
# match nos devuelve None porque no hubo coincidencia al comienzo del texto
print(patron.match(texto))
# match encuentra una coindencia en el comienzo del texto
m = patron.match('foo bar')
m
# search nos devuelve la coincidencia en cualquier ubicacion.
s = patron.search(texto)
s
# findall nos devuelve una lista con todas las coincidencias
fa = patron.findall(texto)
fa
# finditer nos devuelve un iterador
fi = patron.finditer(texto)
fi
# iterando por las distintas coincidencias
next(fi)
next(fi)
Explanation: Ahora que ya tenemos el objeto de expresión regular compilado podemos utilizar alguno de los siguientes métodos para buscar coincidencias con nuestro texto.
match(): El cual determinada si la regex tiene coincidencias en el comienzo del texto.
search(): El cual escanea todo el texto buscando cualquier ubicación donde haya una coincidencia.
findall(): El cual encuentra todos los subtextos donde haya una coincidencia y nos devuelve estas coincidencias como una lista.
finditer(): El cual es similar al anterior pero en lugar de devolvernos una lista nos devuelve un <a href="https://es.wikipedia.org/wiki/Iterador_(patr%C3%B3n_de_dise%C3%B1o)">iterador</a>.
Veamoslos en acción.
End of explanation
# Métodos del objeto de coincidencia
m.group(), m.start(), m.end(), m.span()
s.group(), s.start(), s.end(), s.span()
Explanation: Como podemos ver en estos ejemplos, cuando hay coincidencias, Python nos devuelve un Objeto de coincidencia (salvo por el método findall() que devuelve una lista). Este Objeto de coincidencia también tiene sus propios métodos que nos proporcionan información adicional sobre la coincidencia; éstos métodos son:
group(): El cual devuelve el texto que coincide con la expresion regular.
start(): El cual devuelve la posición inicial de la coincidencia.
end(): El cual devuelve la posición final de la coincidencia.
span(): El cual devuelve una tupla con la posición inicial y final de la coincidencia.
End of explanation
# texto de entrada
becquer = Podrá nublarse el sol eternamente; 
Podrá secarse en un instante el mar; 
Podrá romperse el eje de la tierra 
como un débil cristal. 
¡todo sucederá! Podrá la muerte 
cubrirme con su fúnebre crespón; 
Pero jamás en mí podrá apagarse 
la llama de tu amor.
# patron para dividir donde no encuentre un caracter alfanumerico
patron = re.compile(r'\W+')
palabras = patron.split(becquer)
palabras[:10]  # 10 primeras palabras
# Utilizando la version no compilada de split.
re.split(r'\n', becquer)  # Dividiendo por linea.
# Utilizando el tope de divisiones
patron.split(becquer, 5)
# Cambiando "Podrá" o "podra" por "Puede"
podra = re.compile(r'\b(P|p)odrá\b')
puede = podra.sub("Puede", becquer)
print(puede)
# Limitando el número de reemplazos
puede = podra.sub("Puede", becquer, 2)
print(puede)
# Utilizando la version no compilada de subn
re.subn(r'\b(P|p)odrá\b', "Puede", becquer)  # se realizaron 5 reemplazos
Explanation: Modificando el texto de entrada
Además de buscar coincidencias de nuestro patrón de búsqueda en un texto, podemos utilizar ese mismo patrón para realizar modificaciones al texto de entrada. Para estos casos podemos utilizar los siguientes métodos:
split():  El cual divide el texto en una lista, realizando las divisiones del texto en cada lugar donde se cumple con la expresion regular.
sub(): El cual encuentra todos los subtextos donde existe una coincidencia con la expresion regular y luego los reemplaza con un nuevo texto.
subn(): El cual es similar al anterior pero además de devolver el nuevo texto, también devuelve el numero de reemplazos que realizó.
Veamoslos en acción.
End of explanation
# Ejemplo de findall con la funcion a nivel del modulo
# findall nos devuelve una lista con todas las coincidencias
re.findall(r'\bfoo\b', texto)
Explanation: Funciones no compiladas
En estos últimos ejemplos, pudimos ver casos donde utilizamos las funciones al nivel del módulo split() y subn(). Para cada uno de los ejemplos que vimos (match, search, findall, finditer, split, sub y subn) existe una versión al nivel del módulo que se puede utilizar sin necesidad de compilar primero el patrón de búsqueda; simplemente le pasamos como primer argumento la expresion regular y el resultado será el mismo. La ventaja que tiene la versión compila sobre las funciones no compiladas es que si vamos a utilizar la expresion regular dentro de un bucle nos vamos a ahorrar varias llamadas de funciones y por lo tanto mejorar la performance de nuestro programa.
End of explanation
# Ejemplo de IGNORECASE
# Cambiando "Podrá" o "podra" por "Puede"
podra = re.compile(r'podrá\b', re.I)  # el patrón se vuelve más sencillo
puede = podra.sub("puede", becquer)
print(puede)
# Ejemplo de VERBOSE
mail = re.compile(r
\b             # comienzo de delimitador de palabra
[\w.%+-]       # usuario: Cualquier caracter alfanumerico mas los signos (.%+-)
+@             # seguido de @
[\w.-]         # dominio: Cualquier caracter alfanumerico mas los signos (.-)
+\.            # seguido de .
[a-zA-Z]{2,6}  # dominio de alto nivel: 2 a 6 letras en minúsculas o mayúsculas.
\b             # fin de delimitador de palabra
, re.X)
mails = raul.lopez@relopezbriega.com, Raul Lopez Briega,
foo bar, relopezbriega@relopezbriega.com.ar, raul@github.io, 
https://relopezbriega.com.ar, https://relopezbriega.github.io, 
python@python, river@riverplate.com.ar, pythonAR@python.pythonAR
# filtrando los mails con estructura válida
mail.findall(mails)
Explanation: Banderas de compilación
Las banderas de compilación permiten modificar algunos aspectos de cómo funcionan las expresiones regulares. Todas ellas están disponibles en el módulo re bajo dos nombres, un nombre largo como IGNORECASE y una forma abreviada de una sola letra como I. Múltiples banderas pueden ser especificadas utilizando el operador "|" OR; Por ejemplo, re.I | RE.M establece las banderas de E y M. 
Algunas de las banderas de compilación que podemos encontrar son:
IGNORECASE, I: Para realizar búsquedas sin tener en cuenta las minúsculas o mayúsculas.
VERBOSE, X: Que habilita la modo verborrágico, el cual permite organizar el patrón de búsqueda de una forma que sea más sencilla de entender y leer.
ASCII, A: Que hace que las secuencias de escape \w, \b, \s and \d funciones para coincidencias con los caracteres ASCII.
DOTALL, S: La cual hace que el metacaracter . funcione para cualquier caracter, incluyendo el las líneas nuevas.
LOCALE, L: Esta opción hace que \w, \W, \b, \B, \s, y \S dependientes de la localización actual.
MULTILINE, M: Que habilita la coincidencia en múltiples líneas, afectando el funcionamiento de los metacaracteres ^ and $.
End of explanation
# Accediendo a los grupos por sus indices
patron = re.compile(r"(\w+) (\w+)")
s = patron.search("Raul Lopez")
# grupo 1
s.group(1)
# grupo 2
s.group(2)
Explanation: Como podemos ver en este último ejemplo, la opción VERBOSE puede ser muy util para que cualquier persona que lea nuestra expresion regular pueda entenderla más fácilmente.
Nombrando los grupos
Otra de las funciones interesantes que nos ofrece el módulo re de Python; es la posibilidad de ponerle nombres a los grupos de nuestras expresiones regulares. Así por ejemplo, en lugar de acceder a los grupos por sus índices, como en este caso...
End of explanation
# Accediendo a los grupos por nombres
patron = re.compile(r"(?P<nombre>\w+) (?P<apellido>\w+)")
s = patron.search("Raul Lopez")
# grupo nombre
s.group("nombre")
# grupo apellido
s.group("apellido")
Explanation: Podemos utilizar la sintaxis especial (?P&lt;nombre&gt;patron) que nos ofrece Python para nombrar estos grupos y que sea más fácil identificarlos.
End of explanation
# Validando una URL
url = re.compile(r"^(https?:\/\/)?([\da-z\.-]+)\.([a-z\.]{2,6})([\/\w \.-]*)*\/?$")
# vemos que https://relopezbriega.com.ar lo acepta como una url válida.
url.search("https://relopezbriega.com.ar")
# pero https://google.com/un/archivo!.html no la acepta por el carcter !
print(url.search("https://google.com/un/archivo!.html"))
Explanation: Otros ejemplos de expresiones regulares
Por último, para ir cerrando esta introducción a las expresiones regulares, les dejo algunos ejemplos de las expresiones regulares más utilizadas.
Validando mails
Para validar que un mail tenga la estructura correcta, podemos utilizar la siguiente expresion regular:
regex: \b[\w.%+-]+@[\w.-]+\.[a-zA-Z]{2,6}\b
Este es el patrón que utilizamos en el ejemplo de la opción VERBOSE.
Validando una URL
Para validar que una URL tenga una estructura correcta, podemos utilizar esta expresion regular:
regex: ^(https?:\/\/)?([\da-z\.-]+)\.([a-z\.]{2,6})([\/\w \.-]*)*\/?$
End of explanation
# Validando una dirección IP
patron = ('^(?:(?:25[0-5]|2[0-4][0-9]|'
          '[01]?[0-9][0-9]?)\.){3}'
          '(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$')
ip = re.compile(patron)
# la ip 73.60.124.136 es valida
ip.search("73.60.124.136")
# pero la ip 256.60.124.136 no es valida
print(ip.search("256.60.124.136"))
Explanation: Validando una dirección IP
Para validar que una dirección IP tenga una estructura correcta, podemos utilizar esta expresión regular:
regex: ^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$
End of explanation
# Validando una fecha
fecha = re.compile(r'^(0?[1-9]|[12][0-9]|3[01])/(0?[1-9]|1[012])/((19|20)\d\d)$')
# validando 13/02/1982
fecha.search("13/02/1982")
# no valida 13-02-1982
print(fecha.search("13-02-1982"))
# no valida 32/12/2015
print(fecha.search("32/12/2015"))
# no valida 30/14/2015
print(fecha.search("30/14/2015"))
Explanation: Validando una fecha
Para validar que una fecha tenga una estructura dd/mm/yyyy, podemos utilizar esta expresión regular:
regex: ^(0?[1-9]|[12][0-9]|3[01])/(0?[1-9]|1[012])/((19|20)\d\d)$
End of explanation
<END_TASK>