Cadenas de texto y fechas
Al completar este módulo serás capaz de:
- Manipular cadenas de texto de forma avanzada
- Usar expresiones regulares para buscar y transformar texto
- Trabajar con fechas y horas usando el módulo datetime
- Realizar operaciones con intervalos de tiempo
¿Por qué dominar texto y fechas?
Se estima que más del 80% de los datos del mundo son texto: nombres, direcciones, correos electrónicos, mensajes, publicaciones en redes sociales, documentos… Como programador, vas a pasar mucho tiempo trabajando con texto.
Piensa en las tareas cotidianas de cualquier aplicación:
- Validar que un email tenga el formato correcto antes de enviarlo
- Limpiar los datos que un usuario escribe en un formulario (espacios extra, mayúsculas aleatorias…)
- Extraer información de un texto desordenado (precios de una página web, fechas de un documento)
- Formatear fechas para que se muestren en el idioma del usuario
Este capítulo te dará las herramientas para hacer todo esto de forma elegante y eficiente.
Manipulación de cadenas
Imagina que recibes datos de un formulario web. El usuario escribió " JUAN GARCÍA " (con espacios extra y todo en mayúsculas). Tú necesitas guardarlo como “Juan García”. Las cadenas en Python vienen con un arsenal de métodos para exactamente este tipo de situaciones.
Métodos de búsqueda
Es como usar Ctrl+F en tu editor de texto, pero con superpoderes. Puedes buscar dónde está una palabra, si el texto empieza o termina de cierta forma, cuántas veces aparece algo…
1texto = "Python es un lenguaje de programación muy popular"
2
3# Buscar subcadena
4print(texto.find("lenguaje")) # 14 (posición donde empieza)
5print(texto.find("Java")) # -1 (no encontrado)
6
7# Verificar inicio/final
8print(texto.startswith("Python")) # True
9print(texto.endswith("popular")) # True
10
11# Contar ocurrencias
12print(texto.count("a")) # 4Métodos de transformación
Aquí es donde limpias y transformas el texto. Volviendo al ejemplo del formulario: necesitas quitar espacios, convertir a mayúsculas/minúsculas, reemplazar caracteres… Python tiene un método para cada situación.
1texto = "hola mundo"
2
3print(texto.upper()) # HOLA MUNDO
4print(texto.lower()) # hola mundo
5print(texto.capitalize()) # Hola mundo
6print(texto.title()) # Hola Mundo
7print(texto.swapcase()) # HOLA MUNDO -> hola mundo 1texto = " espacios "
2
3print(texto.strip()) # "espacios" (quita ambos lados)
4print(texto.lstrip()) # "espacios " (solo izquierda)
5print(texto.rstrip()) # " espacios" (solo derecha)
6
7# Rellenar
8numero = "42"
9print(numero.zfill(5)) # "00042"
10print(numero.center(10)) # " 42 "
11print(numero.ljust(10)) # "42 "
12print(numero.rjust(10)) # " 42"1texto = "Me gusta Python. Python es genial."
2
3# Reemplazar todas las ocurrencias
4nuevo = texto.replace("Python", "Java")
5print(nuevo) # Me gusta Java. Java es genial.
6
7# Reemplazar solo N ocurrencias
8nuevo = texto.replace("Python", "Java", 1)
9print(nuevo) # Me gusta Java. Python es genial. 1# Dividir una cadena
2texto = "uno,dos,tres,cuatro"
3partes = texto.split(",")
4print(partes) # ['uno', 'dos', 'tres', 'cuatro']
5
6# Split con límite
7partes = texto.split(",", 2)
8print(partes) # ['uno', 'dos', 'tres,cuatro']
9
10# Dividir líneas
11multilinea = "linea1\nlinea2\nlinea3"
12lineas = multilinea.splitlines()
13print(lineas) # ['linea1', 'linea2', 'linea3']
14
15# Unir una lista en cadena
16palabras = ['Python', 'es', 'genial']
17oracion = ' '.join(palabras)
18print(oracion) # Python es genial
19
20ruta = '/'.join(['home', 'usuario', 'documentos'])
21print(ruta) # home/usuario/documentosFormateo de cadenas
Imagina que quieres mostrar un mensaje como “Hola Ana, tu pedido de 19.99€ está listo”. Podrías hacerlo concatenando strings con +:
1# La forma fea y propensa a errores
2mensaje = "Hola " + nombre + ", tu pedido de " + str(precio) + "€ está listo"Pero esto es tedioso y fácil de equivocarse. Python tiene una forma mucho más elegante: las f-strings (format strings). Simplemente pones una f antes de las comillas y metes las variables entre llaves:
1nombre = "Ana"
2edad = 25
3precio = 19.99
4
5# F-strings (recomendado, Python 3.6+)
6print(f"Hola {nombre}, tienes {edad} años")
7print(f"Precio: {precio:.2f}€") # 2 decimales
8
9# Alineación en f-strings
10print(f"{nombre:<10}") # "Ana " (izquierda)
11print(f"{nombre:>10}") # " Ana" (derecha)
12print(f"{nombre:^10}") # " Ana " (centrado)
13
14# Números
15numero = 1234567
16print(f"{numero:,}") # 1,234,567
17print(f"{numero:_}") # 1_234_567
18
19# Binario, hexadecimal
20x = 255
21print(f"{x:b}") # 11111111 (binario)
22print(f"{x:x}") # ff (hexadecimal)
23print(f"{x:#x}") # 0xff (con prefijo)Expresiones regulares
Las expresiones regulares (regex) tienen fama de ser crípticas e incomprensibles. Pero en realidad son simplemente patrones de búsqueda avanzados. Piensa en ellas como un “detector de metales” para texto: le describes qué forma tiene lo que buscas, y te encuentra todas las coincidencias.
¿Cuándo las necesitas? Cuando los métodos básicos de string se quedan cortos:
- Validar que un email tiene el formato
[email protected] - Extraer todos los precios de un texto (números seguidos de €)
- Encontrar fechas en cualquier formato (06/12/2024, 2024-12-06, 6 de diciembre…)
- Reemplazar patrones complejos, no solo texto literal
1import reFunciones principales
El módulo re te da varias herramientas según lo que necesites hacer:
¿Existe este patrón? Usa search para buscar en cualquier parte del texto, o match para buscar solo al principio.
1import re
2
3texto = "Mi correo es [email protected]"
4
5# search: busca en cualquier parte
6resultado = re.search(r'\w+@\w+\.\w+', texto)
7if resultado:
8 print(resultado.group()) # [email protected]
9
10# match: solo al inicio
11texto2 = "123-456-789"
12if re.match(r'\d{3}', texto2):
13 print("Empieza con 3 dígitos")¿Cuántas coincidencias hay? Usa findall para obtener una lista con todas las coincidencias, o finditer si necesitas más información (posición, etc.).
1import re
2
3texto = "Precios: 10€, 25.50€, 100€"
4
5# findall: lista con todas las coincidencias
6precios = re.findall(r'\d+\.?\d*', texto)
7print(precios) # ['10', '25.50', '100']
8
9# finditer: iterador de objetos Match
10for match in re.finditer(r'\d+\.?\d*', texto):
11 print(f"Encontrado: {match.group()} en posición {match.start()}")¿Quieres reemplazar o dividir? Usa sub para buscar y reemplazar con patrones, o split para dividir por patrones complejos.
1import re
2
3# sub: reemplazar con patrón
4texto = "Hoy es 06-12-2024"
5nuevo = re.sub(r'(\d{2})-(\d{2})-(\d{4})', r'\3/\2/\1', texto)
6print(nuevo) # Hoy es 2024/12/06
7
8# Reemplazo con función
9def doble(match):
10 return str(int(match.group()) * 2)
11
12numeros = "Los números: 5, 10, 15"
13print(re.sub(r'\d+', doble, numeros))
14# Los números: 10, 20, 30
15
16# split con regex
17texto = "uno;dos,tres:cuatro"
18partes = re.split(r'[;,:]', texto)
19print(partes) # ['uno', 'dos', 'tres', 'cuatro']Sintaxis de expresiones regulares
No necesitas memorizar todo esto. Úsalo como referencia cuando construyas tus patrones:
| Patrón | En español | Ejemplo |
|---|---|---|
| . | “cualquier carácter” | a.c encuentra “abc”, “a1c”, “a-c” |
| \d | “un dígito” (0-9) | \d{3} encuentra “123” |
| \w | “una letra, número o _” | \w+ encuentra “hola_123” |
| \s | “un espacio” (espacio, tab, salto de línea) | \s+ encuentra " " |
| ^ | “que empiece por…” | ^Hola solo si el texto empieza con “Hola” |
| $ | “que termine en…” | adiós$ solo si termina en “adiós” |
| * | “cero o más veces” | ab* encuentra “a”, “ab”, “abbb” |
| + | “una o más veces” | ab+ encuentra “ab”, “abbb” (no “a” sola) |
| ? | “opcional” (0 o 1 vez) | colou?r encuentra “color” y “colour” |
| {n} | “exactamente n veces” | \d{4} encuentra años como “2024” |
| {n,m} | “entre n y m veces” | \d{2,4} encuentra “12”, “123”, “1234” |
| [abc] | “cualquiera de estos” | [aeiou] encuentra cualquier vocal |
| [^abc] | “cualquiera excepto estos” | [^0-9] encuentra todo menos dígitos |
| (…) | “agrupa y captura” | (\w+)@(\w+) captura usuario y dominio |
| | | “esto o aquello” | gato|perro encuentra “gato” o “perro” |
Usa r'...' (raw strings) para expresiones regulares. Evita tener que escapar las barras invertidas. O sea, por ejemplo, se puede usar r'\d+' en lugar de '\\d+'.
Ejemplo práctico: validar email
1import re
2
3def validar_email(email):
4 patron = r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$'
5 return bool(re.match(patron, email))
6
7# Pruebas
8print(validar_email("[email protected]")) # True
9print(validar_email("invalido@")) # False
10print(validar_email("[email protected]")) # TrueFechas y horas
¿Cuántos días faltan para tu cumpleaños? ¿Hace cuánto tiempo fue ese evento? ¿Qué día de la semana será el 15 de agosto? Las fechas están en todas partes, y trabajar con ellas puede ser sorprendentemente complicado.
El problema es que las fechas son engañosas. Parece simple: año, mes, día. Pero luego aparecen los husos horarios, los años bisiestos, los meses con diferente número de días, las diferencias entre formatos (¿06/12 es 6 de diciembre o 12 de junio?)…
Python tiene el módulo datetime para manejar toda esta complejidad por ti.
1from datetime import datetime, date, time, timedeltaCrear fechas y horas
Puedes obtener la fecha/hora actual, o crear una fecha específica. Es como mirar el calendario o señalar un día concreto.
1from datetime import datetime, date, time
2
3# Fecha actual
4hoy = date.today()
5print(hoy) # 2024-12-06
6
7# Fecha y hora actual
8ahora = datetime.now()
9print(ahora) # 2024-12-06 17:30:45.123456
10
11# Crear fecha específica
12cumple = date(1995, 8, 15)
13print(cumple) # 1995-08-15
14
15# Crear fecha y hora específica
16evento = datetime(2024, 12, 31, 23, 59, 59)
17print(evento) # 2024-12-31 23:59:59
18
19# Solo hora
20hora = time(14, 30, 0)
21print(hora) # 14:30:00Acceder a componentes
1from datetime import datetime
2
3ahora = datetime.now()
4
5print(ahora.year) # 2024
6print(ahora.month) # 12
7print(ahora.day) # 6
8print(ahora.hour) # 17
9print(ahora.minute) # 30
10print(ahora.second) # 45
11print(ahora.weekday()) # 4 (viernes, 0=lunes)Formatear fechas
Aquí viene uno de los dolores de cabeza clásicos: el formato de las fechas. En España escribimos “06/12/2024” (día/mes/año), pero en EEUU sería “12/06/2024” (mes/día/año). Si tu aplicación tiene usuarios internacionales, necesitas controlar cómo se muestran las fechas.
Python usa códigos de formato (como %d para día, %m para mes) para convertir entre fechas y texto:
graph LR
A["'25/12/2024'<br/>(texto)"] -->|strptime| B["datetime<br/>(objeto)"]
B -->|strftime| C["'December 25, 2024'<br/>(texto)"] 1from datetime import datetime
2
3ahora = datetime.now()
4
5# strftime: de datetime a string (formato → texto)
6print(ahora.strftime("%d/%m/%Y")) # 06/12/2024
7print(ahora.strftime("%H:%M:%S")) # 17:30:45
8print(ahora.strftime("%A, %d de %B")) # Friday, 06 de December
9
10# strptime: de string a datetime
11texto = "2024-12-25 10:30:00"
12navidad = datetime.strptime(texto, "%Y-%m-%d %H:%M:%S")
13print(navidad) # 2024-12-25 10:30:00| Código | Significado | Ejemplo |
|---|---|---|
| %Y | Año (4 dígitos) | 2024 |
| %m | Mes (01-12) | 12 |
| %d | Día (01-31) | 06 |
| %H | Hora (00-23) | 17 |
| %M | Minutos (00-59) | 30 |
| %S | Segundos (00-59) | 45 |
| %A | Día de la semana | Friday |
| %B | Nombre del mes | December |
Operaciones con fechas
Lo más potente de datetime es que puedes sumar y restar tiempo. ¿Qué fecha será dentro de 30 días? ¿Cuántos días han pasado desde tu último cumpleaños? Para esto usas timedelta, que representa un intervalo de tiempo.
1from datetime import datetime, timedelta
2
3ahora = datetime.now()
4
5# Sumar/restar tiempo
6mañana = ahora + timedelta(days=1)
7hace_una_semana = ahora - timedelta(weeks=1)
8en_2_horas = ahora + timedelta(hours=2)
9
10print(f"Mañana: {mañana}")
11print(f"Hace una semana: {hace_una_semana}")
12
13# Diferencia entre fechas
14fecha1 = datetime(2024, 1, 1)
15fecha2 = datetime(2024, 12, 31)
16diferencia = fecha2 - fecha1
17
18print(f"Días en 2024: {diferencia.days}") # 365
19print(f"Segundos totales: {diferencia.total_seconds()}")Comparar fechas
Las fechas se pueden comparar directamente con <, >, ==, etc. Muy útil para verificar si algo ya pasó, si estamos dentro de un período, o cuál de dos fechas es más reciente.
1from datetime import date
2
3fecha1 = date(2024, 6, 15)
4fecha2 = date(2024, 12, 25)
5
6print(fecha1 < fecha2) # True
7print(fecha1 == fecha2) # False
8
9# Verificar si una fecha ya pasó
10hoy = date.today()
11if fecha1 < hoy:
12 print("La fecha ya pasó")¿Cuándo usar cada herramienta?
Después de ver tantas opciones, aquí tienes una guía rápida para elegir:
| Necesitas… | Usa… | Ejemplo |
|---|---|---|
| Buscar/reemplazar texto literal | Métodos de string (find, replace) | Cambiar “Python” por “Java” |
| Buscar patrones complejos | Expresiones regulares (re) | Encontrar todos los emails |
| Validar formato | Regex con match | ¿Es un teléfono válido? |
| Limpiar texto del usuario | Métodos de string (strip, lower) | Quitar espacios, normalizar |
| Fecha/hora actual | datetime.now() | Timestamp de un log |
| Fecha específica | datetime(año, mes, día) | Fecha de nacimiento |
| Sumar/restar tiempo | timedelta | “Dentro de 30 días” |
| Diferencia entre fechas | Resta de datetimes | “Han pasado X días” |
| Convertir texto a fecha | strptime | “25/12/2024” → datetime |
| Convertir fecha a texto | strftime | datetime → “25 de diciembre” |
Empieza con los métodos de string para operaciones simples. Solo usa regex cuando necesites patrones complejos. Para fechas, siempre usa datetime en lugar de manipular strings manualmente.
Ejercicios prácticos
Escribe una función que extraiga todos los números de teléfono de un texto. Formato esperado: 3 dígitos - 3 dígitos - 3 dígitos (ej: 612-345-678)
Crea una función que calcule la edad exacta (años, meses, días) dada una fecha de nacimiento.
Crea una función que reciba un texto desordenado y lo normalice:
- Eliminar espacios múltiples
- Capitalizar oraciones
- Eliminar caracteres especiales excepto puntuación básica