{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Regex 101\n",
    "## Material\n",
    "Por si no te ha dado tiempo a entrar en el enlace antes del taller, puedes hacer cualquiera de estas cosas, lo que tengas más a mano:\n",
    "- ve a la página del taller en la web del T3chFest, ahí está enlazado\n",
    "- ve a `github.com/nimbusaeta/regex-101`\n",
    "\n",
    "Si tienes Jupyter Notebook instalado, tendrás que descargarte el cuaderno, escribir `jupyter notebook` en la terminal y abrirlo en el navegador; si no lo tienes instalado, puedes seguir [este enlace a Drive]() para abrirlo con la herramienta de Google Colaboratory y así poder ejecutar el código."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Quién soy yo y esas cosas\n",
    "Soy Leticia Martín-Fuertes Moreno, estudié Filología Clásica en la Universidad Autónoma de Madrid y trabajo en Google como Associate Linguist (contratada por Adecco). También imparto la asignatura de Introducción a la programación para Humanidades Digitales en el Máster de [Tecnologias de la Información para la Sociedad Digital: Humanidades y Ciencia](https://formacionpermanente.fundacion.uned.es/tp_actividad/idactividad/10576&idioma=es) de la UNED. Coorganizo [R-Ladies Madrid](https://www.meetup.com/es-ES/rladies-madrid/) y [Lingẅars](http://lingwars.github.io/blog/).\n",
    "\n",
    "Tenéis [mi LinkedIn](https://www.linkedin.com/in/leticiamfm/), [mi Twitter](https://twitter.com/nimbusaeta) y [mi GitHub](https://github.com/nimbusaeta) pa lo que queráis.\n",
    "\n",
    "Este taller está basado en uno que di en febrero del año pasado en Medialab Prado, adaptado a su vez de [regular-expressions.info](regular-expressions.info). Personalmente me tocó aprender regex a saco cuando participé en el [proyecto Aracne](https://www.fundeu.es/aracne/), mi primer contacto con la tecnología, y me encantaron. Recopilé [todas las regex que usé para limpiar textos generados por OCR](https://docs.google.com/spreadsheets/d/1OSu0Nd2tyoBNy3XB7HJzxWIgFXuQ8T3-M9d04VK1Rm4/edit?usp=sharing) porque no encontré ninguna recopilación así. Todo esto lo cuento porque puede que algunos ejemplos no sean muy techies precisamente pero espero que sean lo suficientemente ilustrativos."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## ¿Qué son las _regex_ o _expresiones regulares_?\n",
    "Las regex son una serie de patrones y reglas que los gobiernan que sirven básicamente para hacer búsquedas en textos o evaluar si determinada _string_ o cadena de caracteres cumple las condiciones que nos interesen. Muy útiles para trabajar de manera avanzada con texto. Algunas de sus utilidades:\n",
    "- Buscar y reemplazar en código\n",
    "- Validar texto de entrada\n",
    "- Renombrar archivos\n",
    "- Buscar archivos en la línea de comandos\n",
    "- Buscar en bases de datos\n",
    "- Hacer scraping\n",
    "- Limpiar texto\n",
    "\n",
    "Algunas cosas a tener en cuenta sobre las regex:\n",
    "- Es una tecnología vieja, inventada en los 50.\n",
    "- Son multidisciplinares. ¿Quién no ha necesitado alguna vez usar regex, da igual que sea de front, de back, de investigación...\n",
    "- Están muy extendidas, hay muchos programas que los soportan. Por supuesto, todos los lenguajes de programación, pero también programas como Word, Writer, AntConc, TshwaneLex... Lo \"malo\" es que, por esto mismo, también hay muchos \"dialectos\". Aquí vamos a aprenderlas en general, lo que es común a todos los dialectos, y en especial en Python."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Caracteres literales y metacaracteres\n",
    "### Caracteres literales\n",
    "Corresponden con caracteres de la string tal cual. Si ponemos `y` nos encuentra la primera `y` de `Monty Python`, aunque depende de las opciones del programa o la función que estemos usando al programar."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import re\n",
    "\n",
    "a = re.search(\"y\", \"Monty Python\")\n",
    "b = re.findall(\"y\", \"Monty Python\")\n",
    "print(a)\n",
    "print(b)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Metacaracteres\n",
    "De por sí tienen otros significados (que enseguida veremos). Si queremos usarlos de forma literal hay que _escaparlos_. Son los siguientes:\n",
    "    \n",
    "`\\ ^ $ . | ? * + ( ) [ ] { }`"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "a = re.search(\"¿Qué es una almáciga\\?\", \"¿Qué es una almáciga?\")\n",
    "print(a)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Hay caracteres literales que pueden escaparse para darles un uso diferente; también lo vamos a ver."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Una [nota](https://docs.python.org/3/library/re.html#module-re) en la documentación de re sobre el uso de la barra para escapar y la `r` antes de las comillas."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Ejercicio\n",
    "Para encontrar `1+1=2`, ¿qué regex hay que usar?"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "a = re.findall(\"\", \"Todo el mundo sabe que 1+1=2\")\n",
    "print(a)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Sets y rangos de caracteres\n",
    "Con los corchetes cuadrados `[]` creamos un set o conjunto, es decir, buscamos un carácter de entre los que metamos dentro de los corchetes."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "a = re.findall(\"gui[oó]n\", \"Antes escribíamos guión y ahora guion\")\n",
    "print(a)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Si junto a los corchetes usamos el guion `-`, indicamos un rango. Muy útil para capturar, por ejemplo, rangos de números, de letras..."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "indice = \"\"\"1. Prólogo\n",
    "2. Introducción\n",
    "3. Aspectos clave\"\"\"\n",
    "a = re.findall(\"[0-9]\\. \", indice)\n",
    "print(a)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Se puede usar más de un rango a la vez en un mismo set; por ejemplo, podemos hacer que `[a-z]` sea _case-insensitive_ poniendo al lado el rango de letras en mayúsculas: `[a-zA-Z]`"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Los corchetes también nos sirven, junto con el acento circunflejo `^`, para negar caracteres:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "a = re.findall(\"q[^u]\", \"qué quién qé qién\")\n",
    "print(a)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Si metemos más caracteres dentro de los corchetes, niega todos:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "a = re.findall(\"q[^ui]\", \"qué quién qé qién\")\n",
    "print(a)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Ejercicio\n",
    "Para encontrar `a-d`, `”—c` y `ó—”`, ¿qué regex hay que usar? Ojo, que no es lo mismo el guion `-` que la raya `—`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "texto = \"\"\"de ninguna manera-dijo\n",
    "“Señores”—comenzó—”no vamos a…\"\"\"\n",
    "a = re.findall(\"\", texto)\n",
    "print(a)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Repetición y alternancia\n",
    "Con `?` indicamos que el carácter anterior es opcional."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "a = re.findall(\"amigos?\", \"¿Tienes un amigo o tienes muchos amigos?\")\n",
    "print(a)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Con `+` buscamos que el carácter anterior salga una o más veces."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Capturar cualquier conjunto de dos o más espacios\n",
    "a = re.sub(\" +\", \" \", \"Ciudad del  Cabo, una ciudad   de 3,7 millones de  habitantes\")\n",
    "print(a)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "El asterisco `*` se puede entender como una mezcla de `?` y `+`: indicamos que es opcional, pero que, si sale, lo haga una o más veces."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Capturar cualquier palabra entrecomillada\n",
    "a = re.findall('[«\"`][a-zA-Z ]*[´\"»]', '''¿Cuántas veces dicen «ni» los caballeros\n",
    "               que dicen \"ni\" en `Monty Python y los caballeros de la mesa cuadrada´?''')\n",
    "print(a)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Podemos indicar el número exacto de apariciones de caracteres que nos interesan metiéndolo entre las llaves `{}`, o un rango si separamos los números mediante comas."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Capturar cualquier número entre 1000 y 9999\n",
    "a = re.findall(\" [1-9][0-9]{3},\", \"125, 987, 2940, 5982, 13943, 38492, 748392, 404921\")\n",
    "print(a)\n",
    "# Capturar cualquier número entre 100 y 99999\n",
    "a = re.findall(\" [1-9][0-9]{2,4},\", \"125, 987, 2940, 5982, 13943, 38492, 748392, 404921\")\n",
    "print(a)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "La pleca `|` permite la alternancia entre dos opciones:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "a = re.findall(\"Monty Python|the Pythons\", \"\"\"Monty Python and the Holy Grail was based on Arthurian legend\n",
    "                and was directed by Jones and Gilliam. Again, the latter also contributed linking animations \n",
    "                (and put together the opening credits). Along with the rest of the Pythons, Jones and Gilliam \n",
    "                performed several roles in the film, but Chapman took the lead as King Arthur. Cleese returned \n",
    "                to the group for the film, feeling that they were once again breaking new ground. Holy Grail \n",
    "                was filmed on location, in picturesque rural areas of Scotland, with a budget of only £229,000; \n",
    "                the money was raised in part with investments from rock groups such as Pink Floyd, Jethro Tull, \n",
    "                and Led Zeppelin—and UK music industry entrepreneur Tony Stratton-Smith (founder and owner of \n",
    "                the Charisma Records label, for which the Pythons recorded their comedy albums).\"\"\")\n",
    "print(a)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Ejercicios"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Escribamos un traductor balleno-castellano, que sustituya todas las vocales repetidas por una sola vocal. Rellena con regex:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "saludo = \"hoooooooooolaaaaaa, señoooooooora balleeeeeeeeenaaaaaaaaa\"\n",
    "saludo = re.sub(\"\", \"a\", saludo)\n",
    "saludo = re.sub(\"\", \"e\", saludo)\n",
    "saludo = re.sub(\"\", \"i\", saludo)\n",
    "saludo = re.sub(\"\", \"o\", saludo)\n",
    "saludo = re.sub(\"\", \"u\", saludo)\n",
    "print(saludo)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Para encontrar códigos RGB de colores, ¿qué regex tendríamos que escribir?"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "a = re.findall(\"\", \"#63ffed #daffbb #ff787b\")\n",
    "print(a)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Comodines\n",
    "Ciertos caracteres literales se pueden escapar, como hacíamos con los metacaracteres, para usos especiales. Tienen la particularidad de que al ponerlos en mayúsculas, los negamos.\n",
    "- Con `\\d` encontramos dígitos: `\\d` = `[0-9]`; `\\D` = `[^0-9]`\n",
    "- Con `\\w` encontramos caracteres alfanuméricos y la barra baja: `\\w` = `[a-zA-Z0-9_]`; `\\W` = `[^a-zA-Z0-9_]`\n",
    "\n",
    "### Caracteres invisibles\n",
    "- Con `\\t` encontramos el tabulador.\n",
    "- Con `\\n` encontramos el carácter de nueva línea.\n",
    "- Con `\\r` encontramos el carácter de retorno de carro.\n",
    "- Con `\\s` encontramos espacios, tabuladores y saltos de línea: `\\s` = `[ \\t\\r\\n]`; `\\S` = `[^ \\t\\r\\n]`\n",
    "\n",
    "En Windows, por defecto, al crear un nuevo párrafo en los programas de procesamiento de texto, en realidad se están imprimiendo `\\r` y `\\n`. En Linux, se imprime solo `\\n`.\n",
    "### El punto\n",
    "El punto `.` encuentra casi todo: todo menos precisamente los saltos de línea (aunque esto es configurable). ¡Hay que tener mucho cuidado con el punto!"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Ejercicio"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Tenemos una lista de términos sacados de un diccionario médico y queremos deshacernos de prefijos y sufijos. ¿Qué regex hay que usar?"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "entries = [\n",
    "    \"acantocéfalo, la\",\n",
    "    \"acéfalo, la\",\n",
    "    \"bicéfalo, la\",\n",
    "    \"braquicéfalo, la\",\n",
    "    \"bucéfalo\",\n",
    "    \"calocéfalo, la\",\n",
    "    \"céfalo\",\n",
    "    \"cefalo-, -céfalo, la\",\n",
    "    \"cinocéfalo\",\n",
    "    \"dolicocéfalo, la\",\n",
    "    \"encéfalo\",\n",
    "    \"hidrocéfalo, la\",\n",
    "    \"macrocéfalo, la\",\n",
    "    \"mesocéfalo, la\",\n",
    "    \"microcéfalo, la\",\n",
    "    \"policéfalo, la\",\n",
    "    \"termocéfalo, la\",\n",
    "    \"tricéfalo, la\",\n",
    "    \"estomatitis\",\n",
    "    \"faringitis\",\n",
    "    \"fascitis\",\n",
    "    \"flebitis\",\n",
    "    \"flojeritis\",\n",
    "    \"gastritis\",\n",
    "    \"gastroenteritis\",\n",
    "    \"gingivitis\",\n",
    "    \"glositis\",\n",
    "    \"hepatitis\",\n",
    "    \"iritis\",\n",
    "    \"-itis\",\n",
    "    \"laringitis\",\n",
    "    \"linfangitis\",\n",
    "    \"litis\",\n",
    "    \"mastitis\",\n",
    "    \"meningitis\"]\n",
    "\n",
    "entries_clean = []\n",
    "for entry in entries:\n",
    "    if not re.match(\"\", entry):\n",
    "        entries_clean.append(entry)\n",
    "    \n",
    "print(entries_clean)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Anclas\n",
    "Las anclas no se corresponden con ningún carácter, sino con posiciones. Su particularidad reside en que si lo que queremos es reemplazar una string por otra, no tenemos que ponerlos en la cadena meta.\n",
    "\n",
    "El acento circunflejo `^` indica el principio de una línea y `$`, el final. Los límites de las palabras también los podemos encontrar, con `\\b` (y usar su contrario, `\\B`)."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "indice = \"\"\"1. Prólogo\n",
    "2. Introducción\n",
    "3. Aspectos clave\n",
    "4. Historia desde 1979. Un relato único\"\"\"\n",
    "a = re.findall(r\"^[0-9]\\. \", indice, flags=re.M)\n",
    "print(a)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Ejercicio\n",
    "Queremos corregir este error de OCR: to, ta, te, tos, tas, tes a menudo esconden lo, la, le, los, las, les. ¿Qué debemos buscar?"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "texto = \"\"\"todo estaba oscuro\n",
    "era to más parecido\n",
    "una de tas mejores obras\n",
    "según decían, «tes asustaban»\"\"\"\n",
    "\n",
    "resultados = re.findall(r\"\", texto)\n",
    "print(resultados)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Avaricia y pereza\n",
    "Los cuantificadores que hemos visto antes son, por defecto, avariciosos (_greedy_); eso quiere decir que abarcarán todo lo que puedan.\n",
    "\n",
    "Las etiquetas HTML son ejemplos típicos:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "HTML = \"Podemos llamarlas <em>expresiones regulares</em>, <em>regexp</em> o <em>regex</em>.\""
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- La regex `<.+>` a priori es muy suculenta para cazar cada etiqueta, pero:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "a = re.findall(\"<.+>\", HTML)\n",
    "print(a)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- Tampoco nos sirve restringirlo a fragmentos de texto sin espacios:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "a = re.findall(\"<[^ ]+>\", HTML)\n",
    "print(a)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Solución: con `?` la hacemos perezosa (_lazy_); es decir, dejará de buscar tras la primera instancia de `>`. Con `<.+?>` capturamos todas las etiquetas y solo las etiquetas."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "a = re.findall(\"<.+?>\", HTML)\n",
    "print(a)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Ejercicios\n",
    "Encuentra cada oración por separado en esta cita de las _Meditaciones_ de Marco Aurelio:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "a = re.findall(\"\", \"No actúes en la idea de que vas a vivir diez mil años. La necesidad ineludible pende sobre ti. Mientras vives, mientras es posible, sé virtuoso.\")\n",
    "print(a)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Encuentra todas las líneas aéreas:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "airlines = \"\"\"Andes Líneas Aéreas\n",
    "Plus Ultra Líneas Aéreas\n",
    "Líneas Aéreas del Estado\"\"\"\n",
    "\n",
    "a = re.findall(\"\", airlines)\n",
    "print(a)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Agrupación\n",
    "Con los paréntesis `()` se pueden agrupar varios caracteres, útil para:\n",
    "- poder aplicarle a todo ese grupo un mismo cuantificador\n",
    "- capturar ese grupo, es decir, poder usarlo después (en la misma regex de búsqueda o en el reemplazo)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "a = re.search(r\"(for ){2}\", \"Always look for for the bright side of life\")\n",
    "print(a)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "a = re.search(r\"(.+) \\1\", \"Always look for for the bright side of life\")\n",
    "print(a)\n",
    "print(a.group(0))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "a = re.sub(r\"([^ ])[-—]([^ ])\", r\"\\1 - \\2\", \"de ninguna manera-dijo\")\n",
    "print(a)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Ejercicios\n",
    "Capturar por un lado el ancho y por otro el alto en estas medidas:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "a = re.findall(r\"\", \"1280x720\")\n",
    "print(a)\n",
    "a = re.findall(r\"\", \"1920x1600\")\n",
    "print(a)\n",
    "a = re.findall(r\"\", \"1024x768\")\n",
    "print(a)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Ahora, aparte de capturar los errores, queremos sustituirlos por las palabras correctas:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "texto = \"\"\"todo estaba oscuro\n",
    "era to más parecido\n",
    "una de tas mejores obras\n",
    "según decían, «tes asustaban»\"\"\"\n",
    "\n",
    "resultados = re.sub(r\"\", r\"\", texto)\n",
    "print(resultados)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Grupos anidados\n",
    "Los grupos se pueden anidar, es decir, que un fragmento de string puede pertenecer a dos grupos distintos.\n",
    "\n",
    "Imagina que en las siguientes strings queremos capturar tanto el año como el mes y el año:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "a = re.findall(\"(.*(\\d{4}))\", \"\"\"Jan 1987\\nMay 1969\\nAug 2011\"\"\")\n",
    "print(a)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Ejercicios para practicar"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "La regex para encontrar las 3 formas de referirse a las regex es…"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "nombres = [\n",
    "    \"regex\",\n",
    "    \"regexp\",\n",
    "    \"regular expressions\"\n",
    "]\n",
    "\n",
    "for nombre in nombres:\n",
    "    if re.match(\"\", nombre):\n",
    "        print(nombre)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Prefijos en números de teléfono\n",
    "Capturar en grupos el prefijo de varios números de teléfono estadounidenses, con distintos formatos.\n",
    "\n",
    "Prefijos: '415', '650', '416', '202', '403', '416'"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "numeros = \"\"\"415-555-1234\n",
    "650-555-2345\n",
    "(416)555-3456 \n",
    "202 555 4567\n",
    "4035555678\n",
    "1 416 555 9292\"\"\"\n",
    "a = re.findall(\"\", numeros)\n",
    "print(a)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Nombres en direcciones de correo electrónico\n",
    "Capturar en grupos los nombres de distintas direcciones de correo electrónico."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "emails = \"\"\"tom@hogwarts.com\n",
    "tom.riddle@hogwarts.com\n",
    "tom.riddle+t3chfest@hogwarts.com\n",
    "tom@hogwarts.eu.com\n",
    "potter@hogwarts.com\n",
    "harry@hogwarts.com\n",
    "hermione+t3chfest@hogwarts.com\"\"\"\n",
    "a = re.findall(\"\", numeros)\n",
    "print(a)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Recursos\n",
    "- [SketchEngine](https://regex.sketchengine.co.uk/) tiene ejercicios interesantes.\n",
    "- [regex101](https://regex101.com) es perfecto para probar y compartir regex.\n",
    "- [regexone](https://regexone.com) tiene un tutorial interactivo muy útil."
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.0"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
