Introducción
Una expresión regular, o regex, es una herramienta poderosa para el procesamiento de texto. Es una secuencia de caracteres que define un patrón de búsqueda, lo que le permite encontrar, hacer coincidir y gestionar texto con una flexibilidad increíble. En Python, la funcionalidad de expresiones regulares es manejada por el módulo re.
En este laboratorio, aprenderá los fundamentos del uso de expresiones regulares en Python. Comenzaremos con los conceptos básicos de la coincidencia de patrones y avanzaremos gradualmente hacia temas más avanzados como el agrupamiento (grouping), el uso de cuantificadores para la repetición, la definición de clases de caracteres y el aprovechamiento de métodos y flags potentes para manejar escenarios de coincidencia complejos. Al finalizar, se sentirá cómodo utilizando regex para realizar operaciones de cadena sofisticadas.
Introducción a la Coincidencia de Expresiones Regulares (Regex)
En este paso, aprenderás el flujo de trabajo básico para usar expresiones regulares en Python. El proceso generalmente implica cuatro pasos principales:
- Importar el módulo
re. - Crear un objeto Regex usando
re.compile(). Se recomienda usar una cadena sin formato (r"...") para evitar que las barras invertidas se interpreten como caracteres de escape. - Usar el método
search()del objeto Regex para encontrar un patrón en una cadena. Esto devuelve un objetoMatchsi se encuentra el patrón. - Usar el método
group()del objetoMatchpara obtener el texto coincidente real.
Primero, veamos algunos símbolos comunes de regex que utilizarás.
| Símbolo | Coincide con |
|---|---|
? |
cero o una ocurrencia del grupo precedente. |
* |
cero o más ocurrencias del grupo precedente. |
+ |
una o más ocurrencias del grupo precedente. |
{n} |
exactamente n ocurrencias del grupo precedente. |
^spam |
significa que la cadena debe comenzar con spam. |
spam$ |
significa que la cadena debe terminar con spam. |
. |
cualquier carácter, excepto los caracteres de nueva línea. |
\d, \w, y \s |
un dígito, palabra o carácter de espacio, respectivamente. |
\D, \W, y \S |
cualquier cosa excepto un dígito, palabra o espacio, respectivamente. |
[abc] |
cualquier carácter entre los corchetes (como a, b, o c). |
[^abc] |
cualquier carácter que no esté entre los corchetes. |
Ahora, pongamos esto en práctica. Escribiremos un script para encontrar un número de teléfono en una cadena. El patrón para un número de teléfono estadounidense es XXX-XXX-XXXX, donde X es un dígito. En regex, \d representa un dígito.
Nota: Si no estás familiarizado con el uso de WebIDE (similar a VS Code), por favor, apréndelo primero en este laboratorio.
Abre el archivo main.py desde el explorador de archivos de la izquierda y añade el siguiente código.
## Importar el módulo re para operaciones de expresiones regulares
import re
## re.compile(): crear un objeto de patrón regex (usar cadena sin formato r'' para evitar el escape)
## El patrón \d\d\d-\d\d\d-\d\d\d\d coincide con tres dígitos, un guion, tres dígitos, un guion y cuatro dígitos.
phone_num_regex = re.compile(r'\d\d\d-\d\d\d-\d\d\d\d')
## El método search() escanea la cadena, buscando la primera ubicación donde esta regex produce una coincidencia.
mo = phone_num_regex.search('My number is 415-555-4242.')
## El método group() devuelve la cadena del texto coincidente.
print(f'Phone number found: {mo.group()}')
Para ejecutar el script, abre una terminal y ejecuta el siguiente comando:
python3 main.py
Deberías ver el número de teléfono coincidente impreso en la consola.
Phone number found: 415-555-4242
Este flujo de trabajo básico es la base para todas las operaciones de regex en Python.
Agrupación y Alternancia
En este paso, aprenderá cómo aislar partes específicas de su texto coincidente utilizando grupos y cómo hacer coincidir una de varias expresiones posibles utilizando la alternancia.
Agrupación con Paréntesis ()
Los paréntesis () se utilizan para crear grupos dentro de una expresión regular. Esto es útil cuando desea extraer partes de una coincidencia, como el código de área de un número de teléfono. Puede acceder a grupos individuales con group(n), donde group(1) es el primer grupo, group(2) es el segundo, y así sucesivamente. group(0) o group() devuelve la cadena completa que coincidió.
Modifiquemos main.py para separar el código de área del resto del número de teléfono.
import re
## Parentheses create groups: group(1) for the first group, group(2) for the second.
## The first group (\d\d\d) captures the area code.
## The second group (\d\d\d-\d\d\d\d) captures the main number.
phone_num_regex = re.compile(r'(\d\d\d)-(\d\d\d-\d\d\d\d)')
mo = phone_num_regex.search('My number is 415-555-4242.')
## To retrieve all groups at once, use the groups() method, which returns a tuple.
area_code, main_number = mo.groups()
print(f"Area Code: {area_code}")
print(f"Main Number: {main_number}")
print(f"Full Match: {mo.group(0)}")
Ejecute el script:
python3 main.py
La salida mostrará las partes separadas del número:
Area Code: 415
Main Number: 555-4242
Full Match: 415-555-4242
Alternancia con la Barra Vertical |
El carácter de barra vertical | actúa como un operador "O" (OR). Le permite hacer coincidir una de varias expresiones. Por ejemplo, Batman|Tina Fey coincidirá con "Batman" o con "Tina Fey".
También puede usar la barra vertical dentro de un grupo para hacer coincidir uno de varios patrones posibles como parte de su regex. Intentemos hacer coincidir diferentes términos relacionados con "Bat-". Actualice su main.py con el siguiente código:
import re
## The pipe | matches one of several patterns within a group.
## Here, it matches 'man', 'mobile', 'copter', or 'bat'.
bat_regex = re.compile(r'Bat(man|mobile|copter|bat)')
mo = bat_regex.search('Batmobile lost a wheel')
print(f"Full match: {mo.group()}")
print(f"Group 1: {mo.group(1)}")
Ejecute el script nuevamente:
python3 main.py
Verá que la regex coincidió con "Batmobile", y el primer grupo capturó "mobile".
Full match: Batmobile
Group 1: mobile
Repetición con Cuantificadores
En este paso, exploraremos los cuantificadores, que son caracteres especiales que le permiten especificar cuántas veces debe repetirse un grupo precedente.
Coincidencia Opcional con ?
El signo de interrogación ? hace que el grupo precedente sea opcional, lo que significa que coincidirá cero o una vez. Esto es perfecto para patrones que pueden o no aparecer. Por ejemplo, podemos hacer coincidir tanto "Batman" como "Batwoman".
Actualice main.py con este código:
import re
## The (wo)? group is optional. It will match 'wo' if it exists, or match nothing if it doesn't.
bat_regex = re.compile(r'Bat(wo)?man')
mo1 = bat_regex.search('The Adventures of Batman')
print(mo1.group())
mo2 = bat_regex.search('The Adventures of Batwoman')
print(mo2.group())
Ejecute el script:
python3 main.py
La salida muestra que ambas cadenas coincidieron con éxito:
Batman
Batwoman
Cero o Más con *
El asterisco * (o estrella) coincide con el grupo precedente cero o más veces. Esto es útil cuando un patrón puede repetirse cualquier número de veces, incluyendo ninguna.
import re
## The (wo)* group can appear zero or more times.
bat_regex = re.compile(r'Bat(wo)*man')
mo1 = bat_regex.search('The Adventures of Batman') ## Zero 'wo'
print(mo1.group())
mo2 = bat_regex.search('The Adventures of Batwowowowoman') ## Four 'wo's
print(mo2.group())
Uno o Más con +
El signo más + coincide con el grupo precedente una o más veces. A diferencia de la estrella, el grupo debe aparecer al menos una vez.
import re
## The (wo)+ group must appear at least once.
bat_regex = re.compile(r'Bat(wo)+man')
mo1 = bat_regex.search('The Adventures of Batwoman') ## One 'wo'
print(mo1.group())
mo2 = bat_regex.search('The Adventures of Batman') ## Zero 'wo's
print(mo2 is None) ## This will print True because no match is found.
Repeticiones Específicas con {}
Las llaves {} le permiten especificar un número exacto de repeticiones, o un rango.
{3}coincide exactamente tres veces.{3,5}coincide entre tres y cinco veces.{3,}coincide tres o más veces.{,5}coincide hasta cinco veces.
import re
## (Ha){3} matches 'Ha' repeated exactly 3 times.
ha_regex = re.compile(r'(Ha){3}')
mo1 = ha_regex.search('HaHaHa')
print(mo1.group())
Coincidencia Codiciosa (Greedy) y No Codiciosa (Non-Greedy)
Por defecto, la regex de Python es "codiciosa" (greedy), lo que significa que coincide con la cadena más larga posible. Para hacerla "no codiciosa" (non-greedy, que coincida con la cadena más corta posible), agregue un signo de interrogación ? después del cuantificador.
Veamos la diferencia. Borre main.py y agregue lo siguiente:
import re
## Greedy match: {3,5} matches the longest possible string (5 'Ha's).
greedy_ha_regex = re.compile(r'(Ha){3,5}')
mo1 = greedy_ha_regex.search('HaHaHaHaHa')
print(f"Greedy match: {mo1.group()}")
## Non-greedy match: {3,5}? matches the shortest possible string (3 'Ha's).
non_greedy_ha_regex = re.compile(r'(Ha){3,5}?')
mo2 = non_greedy_ha_regex.search('HaHaHaHaHa')
print(f"Non-greedy match: {mo2.group()}")
Ejecute el script:
python3 main.py
La salida muestra claramente la diferencia:
Greedy match: HaHaHaHaHa
Non-greedy match: HaHaHa
Clases de Caracteres, Anclas y Comodines
En este paso, aprenderá técnicas más avanzadas, incluyendo cómo encontrar todas las coincidencias, crear conjuntos de caracteres personalizados, anclar coincidencias al inicio o final de una cadena y usar comodines.
El Método findall()
Mientras que search() devuelve solo la primera coincidencia, el método findall() devuelve una lista de todas las coincidencias no superpuestas en la cadena. Si la regex tiene grupos, findall() devuelve una lista de tuplas, donde cada tupla contiene las cadenas para cada grupo.
Actualice main.py con este ejemplo:
import re
## If the regex has no groups, findall() returns a list of strings.
phone_num_regex = re.compile(r'\d\d\d-\d\d\d-\d\d\d\d')
matches = phone_num_regex.findall('Cell: 415-555-9999 Work: 212-555-0000')
print(matches)
Ejecute el script para ver la salida:
python3 main.py
['415-555-9999', '212-555-0000']
Crear Sus Propias Clases de Caracteres []
Los corchetes [] le permiten definir una clase de caracteres personalizada que coincidirá con cualquier carácter individual dentro de ellos. Por ejemplo, [aeiou] coincide con cualquier vocal minúscula. También puede especificar un rango, como [a-z] para cualquier letra minúscula o [0-9] para cualquier dígito.
Un acento circunflejo ^ como primer carácter dentro de los corchetes crea una clase de caracteres negativa, que coincide con cualquier carácter que no esté en la clase.
Encontremos todas las vocales en una cadena. Actualice main.py:
import re
## [aeiouAEIOU] matches any vowel, lowercase or uppercase.
vowel_regex = re.compile(r'[aeiouAEIOU]')
vowels = vowel_regex.findall('Robocop eats baby food. BABY FOOD.')
print(vowels)
Anclas: Acento Circunflejo ^ y Signo de Dólar $
Las anclas se utilizan para afirmar una posición en la cadena.
- El acento circunflejo
^coincide con el inicio de la cadena. - El signo de dólar
$coincide con el final de la cadena.
Cuando se usan juntas (^...$), fuerzan a que toda la cadena coincida con el patrón.
import re
## ^Hello matches strings that start with 'Hello'.
begins_with_hello = re.compile(r'^Hello')
match1 = begins_with_hello.search('Hello world!')
print(match1 is not None) ## True
## \d+$ matches strings that end with one or more digits.
ends_with_number = re.compile(r'\d+$')
match2 = ends_with_number.search('Your number is 42')
print(match2.group()) ## '42'
## ^\d+$ matches strings that consist only of digits.
whole_string_is_num = re.compile(r'^\d+$')
match3 = whole_string_is_num.search('1234567890')
print(match3 is not None) ## True
El Carácter Comodín .
El carácter punto . (o comodín) coincide con cualquier carácter individual excepto un salto de línea (newline). Es un atajo útil para "cualquier carácter".
Encontremos todas las palabras de tres letras que terminan en "at". Actualice main.py:
import re
## The . character matches any character (except newline).
## So, .at matches 'cat', 'hat', 'sat', etc.
at_regex = re.compile(r'.at')
matches = at_regex.findall('The cat in the hat sat on the flat mat.')
print(matches)
Ejecute el script:
python3 main.py
La salida será una lista de todas las palabras coincidentes:
['cat', 'hat', 'sat', 'lat', 'mat']
Métodos Avanzados y Banderas (Flags)
En este paso final, cubriremos algunos métodos y banderas potentes que modifican el comportamiento de sus expresiones regulares (regular expressions o regex), haciéndolas aún más versátiles.
Coincidir con Todo con Punto-Asterisco .*
La combinación .* es un patrón común que coincide con cualquier carácter (.) repetido cero o más veces (*). Se utiliza para hacer coincidir cualquier secuencia de caracteres. Por defecto, es codicioso (greedy) y coincidirá con la mayor cantidad de texto posible.
import re
## The pattern (.*) captures everything between 'First Name: ' and ' Last Name: '.
name_regex = re.compile(r'First Name: (.*) Last Name: (.*)')
mo = name_regex.search('First Name: Al Last Name: Sweigart')
print(f"First Name: {mo.group(1)}")
print(f"Last Name: {mo.group(2)}")
Para hacerlo no codicioso (non-greedy), use .*?. Esto coincidirá con la cadena más corta posible.
Coincidir con Saltos de Línea con re.DOTALL
Por defecto, el carácter punto . no coincide con los caracteres de salto de línea (\n). Para cambiar esto, puede pasar re.DOTALL como segundo argumento a re.compile(). Esta bandera permite que . coincida con todos los caracteres, incluidos los saltos de línea.
import re
## With re.DOTALL, '.*' will match across multiple lines.
newline_regex = re.compile('.*', re.DOTALL)
mo = newline_regex.search('Serve the public trust.\nProtect the innocent.\nUphold the law.')
print(mo.group())
Coincidencia Insensible a Mayúsculas y Minúsculas con re.IGNORECASE
Para hacer que su regex ignore las mayúsculas y minúsculas (por ejemplo, que coincida con "cat", "Cat" y "CAT"), pase re.IGNORECASE o su abreviatura re.I como segundo argumento a re.compile().
import re
## re.I makes the pattern 'robocop' match 'Robocop', 'ROBOCOP', etc.
robocop = re.compile(r'robocop', re.I)
match = robocop.search('Robocop is part man, part machine, all cop.')
print(match.group())
Sustitución de Cadenas con el Método sub()
El método sub() encuentra todas las ocurrencias de un patrón y las reemplaza con una cadena especificada. Toma dos argumentos: la cadena de reemplazo y la cadena donde buscar.
Reemplacemos los nombres de los agentes con "CENSORED". Actualice main.py:
import re
## The pattern 'Agent \w+' matches 'Agent ' followed by one or more word characters.
names_regex = re.compile(r'Agent \w+')
result = names_regex.sub('CENSORED', 'Agent Alice gave the secret documents to Agent Bob.')
print(result)
Ejecute el script:
python3 main.py
La salida mostrará la cadena sustituida:
CENSORED gave the secret documents to CENSORED.
Gestión de Regex Complejas con re.VERBOSE
Cuando los patrones de regex se vuelven complejos, pueden ser difíciles de leer. Al pasar re.VERBOSE a re.compile(), puede escribir su regex en varias líneas y agregar comentarios para explicar las diferentes partes. Los espacios en blanco y los comentarios (#) dentro de la cadena se ignoran.
Esta regex compleja:
phone_regex = re.compile(r'((\d{3}|\(\d{3}\))?(\s|-|\.)?\d{3}(\s|-|\.)\d{4}(\s*(ext|x|ext.)\s*\d{2,5})?)')
Se convierte en esta versión legible con re.VERBOSE:
import re
phone_regex = re.compile(r'''(
(\d{3} | \(\d{3}\))? ## area code (optional)
(\s | - | \.)? ## separator (optional)
\d{3} ## first 3 digits
(\s | - | \.) ## separator
\d{4} ## last 4 digits
(\s*(ext|x|ext.)\s*\d{2,5})? ## extension (optional)
)''', re.VERBOSE)
mo = phone_regex.search("My phone number is 415-555-4242 ext. 55")
if mo:
print(f"Found: {mo.group()}")
Esto hace que su código sea mucho más fácil de mantener.
Resumen
En este laboratorio, ha dominado los fundamentos de las expresiones regulares (regular expressions o regex) en Python utilizando el módulo re. Aprendió el flujo de trabajo completo, desde compilar un patrón con re.compile() hasta encontrar coincidencias con search() y recuperar texto con group().
Exploró conceptos clave que incluyen:
- Agrupación con
()para capturar partes específicas de una coincidencia. - Alternancia con
|para hacer coincidir uno de varios patrones. - Cuantificadores como
?,*,+, y{}para controlar la repetición. - Coincidencia Codiciosa (Greedy) vs. No Codiciosa (Non-greedy) para controlar la longitud de las coincidencias.
- Herramientas avanzadas como
findall()para obtener todas las coincidencias, clases de caracteres[]para conjuntos personalizados, y anclas^y$para fijar coincidencias a los límites de la cadena. - Banderas (Flags) potentes como
re.DOTALLpara coincidencias multilínea,re.IGNORECASEpara la insensibilidad a mayúsculas y minúsculas, yre.VERBOSEpara escribir regex limpias y comentadas. - Sustitución de cadenas con el método
sub().
Con estas habilidades, ahora está equipado para manejar una amplia gama de tareas de procesamiento de texto, desde la validación simple de datos hasta la extracción y manipulación compleja de datos.



