Cadenas de texto y fechas

Objetivos del módulo

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"))           # 4

Mé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/documentos

Formateo 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 re

Funciones 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”
Cadenas raw

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]"))  # True

Fechas 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, timedelta

Crear 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:00

Acceder 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”
Regla general

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

Ejercicio 1: Extraer información de texto

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)

 1import re
 2
 3def extraer_telefonos(texto):
 4    patron = r'\d{3}-\d{3}-\d{3}'
 5    return re.findall(patron, texto)
 6
 7texto = """
 8Contactos:
 9- Juan: 612-345-678
10- María: 698-123-456
11- Oficina: 91-123-4567 (no válido)
12- Pedro: 666-777-888
13"""
14
15telefonos = extraer_telefonos(texto)
16print(telefonos)  # ['612-345-678', '698-123-456', '666-777-888']
Ejercicio 2: Calculadora de edad

Crea una función que calcule la edad exacta (años, meses, días) dada una fecha de nacimiento.

 1from datetime import date
 2
 3def calcular_edad(fecha_nacimiento):
 4    hoy = date.today()
 5    
 6    años = hoy.year - fecha_nacimiento.year
 7    meses = hoy.month - fecha_nacimiento.month
 8    dias = hoy.day - fecha_nacimiento.day
 9    
10    # Ajustar si el día aún no llegó este mes
11    if dias < 0:
12        meses -= 1
13        # Días del mes anterior
14        mes_anterior = hoy.month - 1 if hoy.month > 1 else 12
15        dias_mes = 30 if mes_anterior in [4,6,9,11] else 31
16        if mes_anterior == 2:
17            dias_mes = 28
18        dias += dias_mes
19    
20    # Ajustar si el mes aún no llegó este año
21    if meses < 0:
22        años -= 1
23        meses += 12
24    
25    return años, meses, dias
26
27# Ejemplo
28nacimiento = date(1995, 8, 15)
29años, meses, dias = calcular_edad(nacimiento)
30print(f"Edad: {años} años, {meses} meses y {dias} días")
Ejercicio 3: Limpiar y normalizar texto

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
 1import re
 2
 3def normalizar_texto(texto):
 4    # Eliminar caracteres especiales (dejando letras, números, espacios y puntuación)
 5    texto = re.sub(r'[^\w\s.,!?¿¡]', '', texto)
 6    
 7    # Reducir espacios múltiples a uno solo
 8    texto = re.sub(r'\s+', ' ', texto)
 9    
10    # Eliminar espacios al inicio y final
11    texto = texto.strip()
12    
13    # Capitalizar después de . ! ?
14    def capitalizar(match):
15        return match.group(1) + match.group(2).upper()
16    
17    texto = re.sub(r'([.!?]\s*)(\w)', capitalizar, texto)
18    
19    # Capitalizar el inicio
20    if texto:
21        texto = texto[0].upper() + texto[1:]
22    
23    return texto
24
25# Prueba
26texto_sucio = "   hola!!!   esto    es   UN texto** desordenado.  necesita   LIMPIEZA   "
27print(normalizar_texto(texto_sucio))
28# "Hola! Esto es UN texto desordenado. Necesita LIMPIEZA"

Quiz

🎮 Quiz: Cadenas y fechas

0 / 0
Cargando preguntas...

Anterior: Estructuras de datos Siguiente: Ficheros