Strings en Python: métodos, f-strings y formateo de cadenas
- Strings en Python: inmutables y Unicode
- Crear y acceder: índices y slicing
- Búsqueda: find, index, count, in
- Transformar: upper, lower, strip, replace
- split() y join(): dividir y reunir
- Validar: isdigit, isalpha, startswith…
- f-strings y formateo avanzado
- Cadenas multilínea y raw strings
- Unicode, encode y decode
- Programa completo: analizador de texto
- Errores clásicos
- Preguntas frecuentes
str en Python es esa misma intuición hecha realidad: una cadena inmutable de puntos Unicode que puedes dividir, transformar, buscar y reconstruir con una precisión que Ada habría reconocido como pura álgebra.
Los strings (str) son el tipo de dato más ubicuo en Python: mensajes de usuario, ficheros de texto, respuestas de API, URLs, consultas SQL… todo pasa por ellos. Python incluye más de cuarenta métodos de cadena integrados y el sistema de f-strings más expresivo de cualquier lenguaje moderno. Esta lección cubre desde slicing hasta formateo avanzado con especificadores de formato.
🔡 Crear y acceder: índices y slicing
# Tres formas de crear strings
simple = 'Hola mundo'
doble = "Hola mundo" # equivalentes en Python
multiline = """Línea 1
Línea 2
Línea 3"""
# Los strings son secuencias: acceso por índice
s = "Python"
s[0] # 'P' — primer carácter
s[-1] # 'n' — último carácter
s[-2] # 'o' — penúltimo
len(s) # 6
# Slicing: s[inicio:fin:paso] (fin es excluido)
s[0:3] # 'Pyt' — caracteres 0, 1, 2
s[2:] # 'thon' — desde el índice 2 hasta el final
s[:4] # 'Pyth' — desde el inicio hasta el 3
s[-3:] # 'hon' — últimos 3 caracteres
s[::-1] # 'nohtyP' — invertir el string ✅
# Slicing con paso
s[::2] # 'Pto' — cada 2 caracteres
s[1::2] # 'yhn' — desde índice 1, cada 2
# Los strings son INMUTABLES — no se pueden modificar in-place
s[0] = 'p' # TypeError: 'str' object does not support item assignment
s = 'p' + s[1:] # ✅ crear uno nuevo: 'python'
# Concatenación y repetición
"Hola" + " " + "mundo" # 'Hola mundo'
"Ha" * 3 # 'HaHaHa'
"-" * 40 # '----------------------------------------'
# Pertenencia
"Py" in "Python" # True
"Java" not in "Python" # True
🔍 Búsqueda: find, index, count, in
texto = "La programación en Python es elegante y poderosa"
# find() → devuelve el índice de la primera aparición, -1 si no existe
texto.find("Python") # 19
texto.find("Java") # -1
texto.find("a") # 1 ← primera 'a'
texto.find("a", 5) # 9 ← primera 'a' a partir del índice 5
texto.find("a", 5, 20) # 9 ← entre índices 5 y 20
# index() → igual que find() pero lanza ValueError si no encuentra
texto.index("Python") # 19
texto.index("Java") # ValueError: substring not found
# rfind() y rindex() — buscan desde el final
texto.rfind("a") # 47 ← última 'a'
# count() — cuántas veces aparece
texto.count("a") # 5
texto.count("en") # 2
texto.count("xx") # 0
# Operador in (recomendado para comprobar existencia)
"Python" in texto # True — más legible que find() != -1
"Java" in texto # False
# startswith() y endswith()
texto.startswith("La") # True
texto.startswith("la") # False ← sensible a mayúsculas
texto.endswith("osa") # True
# Tupla de prefijos/sufijos posibles
texto.startswith(("La", "El", "Los")) # True — coincide con "La"
nombre = "imagen.jpg"
nombre.endswith((".jpg", ".jpeg", ".png", ".webp")) # True ✅
in para comprobar existencia (más legible). Usa find() cuando necesites la posición y la ausencia es un caso válido. Usa index() solo cuando el elemento debe existir y quieres que el programa falle ruidosamente si no es así.
🔧 Transformar: upper, lower, strip, replace
s = " ¡Hola, Mundo! "
# Mayúsculas y minúsculas
s.upper() # ' ¡HOLA, MUNDO! '
s.lower() # ' ¡hola, mundo! '
s.capitalize() # ' ¡hola, mundo! ' ← solo primera letra del string
s.title() # ' ¡Hola, Mundo! ' ← primera letra de cada palabra
s.swapcase() # ' ¡hOLA, mUNDO! '
# Eliminar espacios (y otros caracteres)
s.strip() # '¡Hola, Mundo!' — quita espacios inicio y fin
s.lstrip() # '¡Hola, Mundo! ' — solo inicio
s.rstrip() # ' ¡Hola, Mundo!' — solo fin
"...hola...".strip(".") # 'hola' — quita el carácter indicado
"xxhola".lstrip("x") # 'hola'
# Sustituir
texto = "Me gusta Java. Java es potente."
texto.replace("Java", "Python") # 'Me gusta Python. Python es potente.'
texto.replace("Java", "Python", 1) # 'Me gusta Python. Java es potente.' ← solo 1ª vez
# Eliminar un carácter: replace con string vacío
"hola mundo".replace(" ", "") # 'holamundo'
"precio: 9,99€".replace("€", "").replace(",", ".") # 'precio: 9.99'
# center, ljust, rjust — alineación
"Python".center(20) # ' Python '
"Python".center(20, "-") # '-------Python-------'
"Python".ljust(20, ".") # 'Python..............'
"Python".rjust(20) # ' Python'
# zfill — rellenar con ceros a la izquierda
"42".zfill(6) # '000042'
str(7).zfill(3) # '007'
# expandtabs — sustituir \t por espacios
"col1\tcol2\tcol3".expandtabs(10) # alinea columnas con tabulaciones de 10
✂️ split() y join(): dividir y reunir
# split() — divide un string en lista
"hola mundo python".split() # ['hola', 'mundo', 'python']
"hola mundo python".split(" ") # ['hola', 'mundo', 'python']
# Con separador explícito
"uno,dos,tres".split(",") # ['uno', 'dos', 'tres']
"a::b::c".split("::") # ['a', 'b', 'c']
"2026-03-09".split("-") # ['2026', '03', '09']
# Limitar el número de divisiones
"a b c d e".split(" ", 2) # ['a', 'b', 'c d e']
# rsplit() — divide desde la derecha
"dir/subdir/archivo.txt".rsplit("/", 1) # ['dir/subdir', 'archivo.txt']
# splitlines() — divide por saltos de línea
"línea1\nlínea2\nlínea3".splitlines() # ['línea1', 'línea2', 'línea3']
# partition() — divide en exactamente 3 partes en la primera aparición
"usuario@dominio.com".partition("@") # ('usuario', '@', 'dominio.com')
"sin-arroba".partition("@") # ('sin-arroba', '', '')
# join() — une una lista en un string ← el patrón más importante
palabras = ["Python", "es", "genial"]
" ".join(palabras) # 'Python es genial'
"-".join(palabras) # 'Python-es-genial'
"".join(palabras) # 'Pythonesgeni al'
"\n".join(palabras) # 'Python\nes\ngenial'
# join() es O(n), concatenación en bucle es O(n²) — ¡usar siempre join!
# ❌ Lento (O(n²)):
resultado = ""
for palabra in palabras:
resultado += palabra + " "
# ✅ Rápido (O(n)):
resultado = " ".join(palabras)
# Patrón completo split → procesar → join
csv_line = "Ana,28,Madrid,Python"
campos = csv_line.split(",")
campos[1] = str(int(campos[1]) + 1) # incrementar edad
nueva_linea = ",".join(campos) # 'Ana,29,Madrid,Python'
✅ Validar: isdigit, isalpha, startswith…
# Métodos is* — devuelven True/False
"12345".isdigit() # True — solo dígitos (0-9 y equivalentes Unicode)
"12.5".isdigit() # False — el punto no es un dígito
"hola".isalpha() # True — solo letras
"hola123".isalpha() # False
"hola123".isalnum() # True — letras y dígitos
" ".isspace() # True — solo espacios en blanco
"HOLA".isupper() # True
"hola".islower() # True
"Hola Mundo".istitle() # True — cada palabra comienza en mayúscula
# Validaciones prácticas
def es_numero_entero(s):
return s.lstrip("+-").isdigit() # acepta "+42", "-7"
def es_identificador_valido(s):
return s.isidentifier() # True si es nombre de variable válido
"mi_var_1".isidentifier() # True
"1variable".isidentifier() # False — no puede empezar por dígito
"mi-var".isidentifier() # False — guión no permitido
# isprintable() — sin caracteres de control
"hola\x00".isprintable() # False — carácter nulo
"hola".isprintable() # True
# isascii() — solo caracteres ASCII (< 128)
"hello".isascii() # True
"héllo".isascii() # False — é es Unicode > 127
# Validar extensión de archivo
def es_imagen(nombre_archivo):
EXTENSIONES = (".jpg", ".jpeg", ".png", ".gif", ".webp", ".svg")
return nombre_archivo.lower().endswith(EXTENSIONES)
es_imagen("foto.JPG") # True
es_imagen("datos.csv") # False
✨ f-strings y formateo avanzado
# f-strings (Python 3.6+) — la forma moderna y recomendada
nombre = "Ana"
edad = 28
ciudad = "Madrid"
f"Hola, {nombre}!" # 'Hola, Ana!'
f"{nombre} tiene {edad} años" # 'Ana tiene 28 años'
f"{nombre.upper()} vive en {ciudad}" # 'ANA vive en Madrid'
# Expresiones arbitrarias dentro de {}
f"{2 ** 10}" # '1024'
f"{edad * 365} días de vida" # '10220 días de vida'
f"{'Sí' if edad >= 18 else 'No'} mayor" # 'Sí mayor'
# Especificadores de formato: {valor:especificador}
pi = 3.14159265358979
f"{pi:.2f}" # '3.14' — 2 decimales
f"{pi:.5f}" # '3.14159'
f"{pi:10.3f}" # ' 3.142' — ancho 10, 3 decimales, alineado derecha
f"{pi:<10.3f}" # '3.142 ' — alineado izquierda
f"{pi:^10.3f}" # ' 3.142 ' — centrado
# Enteros
n = 1234567
f"{n:,}" # '1,234,567' — separador de miles
f"{n:_}" # '1_234_567' — separador con guión bajo
f"{n:010d}" # '0001234567' — rellenar con ceros
f"{n:+d}" # '+1234567' — signo explícito
f"{255:b}" # '11111111' — binario
f"{255:x}" # 'ff' — hexadecimal minúsculas
f"{255:X}" # 'FF' — hexadecimal mayúsculas
f"{255:o}" # '377' — octal
f"{255:#x}" # '0xff' — hex con prefijo
# Porcentajes
ratio = 0.8547
f"{ratio:.1%}" # '85.5%'
f"{ratio:.0%}" # '85%'
# Fechas con datetime
from datetime import datetime
ahora = datetime.now()
f"{ahora:%d/%m/%Y}" # '09/03/2026'
f"{ahora:%H:%M:%S}" # '14:32:07'
f"{ahora:%A, %d de %B}" # 'Monday, 09 de March'
# Debug con = (Python 3.8+) — imprime nombre y valor
x = 42
f"{x = }" # 'x = 42' ← muy útil para depurar
f"{pi = :.3f}" # 'pi = 3.142'
# Alineación con fill y align
f"{'Python':*^20}" # '*******Python*******'
f"{'izq':<10}" # 'izq '
f"{'der':>10}" # ' der'
# Cadenas de formato reutilizables con format()
plantilla = "Hola, {}. Tienes {} mensajes."
plantilla.format("Ana", 5) # 'Hola, Ana. Tienes 5 mensajes.'
plantilla.format("Luis", 0) # 'Hola, Luis. Tienes 0 mensajes.'
# Con posiciones y nombres
"{0} y {1}, o {1} y {0}".format("A", "B") # 'A y B, o B y A'
"{nombre} tiene {edad} años".format(nombre="Ana", edad=28)
📜 Cadenas multilínea y raw strings
# Strings multilínea con triple comilla
sql = """
SELECT nombre, email, ciudad
FROM usuarios
WHERE activo = 1
AND ciudad = 'Madrid'
ORDER BY nombre
"""
html = """
{titulo}
{descripcion}
""".format(titulo="Python", descripcion="El lenguaje más versátil")
# Concatenación implícita de literales adyacentes (sin operador)
mensaje = ("Esta es una cadena muy larga que "
"se divide en varias líneas "
"sin usar backslash ni multilínea.")
# Backslash para continuar en la siguiente línea
mensaje2 = "Esta es una cadena muy larga que " \
"continúa aquí."
# Raw strings r"..." — las barras invertidas no se interpretan
ruta_win = r"C:\Users\Ana\Desktop\proyecto" # tal cual
regex = r"\d{4}-\d{2}-\d{2}" # más legible que "\\d{4}-\\d{2}-\\d{2}"
# Comparación: string normal vs raw string
print("\n") # imprime un salto de línea
print(r"\n") # imprime la barra y la n literalmente: \n
# Bytes vs strings
texto = "hola" # str (Unicode)
bytes_ = b"hola" # bytes (ASCII/bytes crudos)
# Conversión
texto.encode("utf-8") # b'hola'
b"hola".decode("utf-8") # 'hola'
"héllo".encode("utf-8") # b'h\xc3\xa9llo'
"héllo".encode("latin-1") # b'h\xe9llo'
🌍 Unicode, encode y decode
import unicodedata
# Python 3 usa Unicode (UTF-32 internamente) para todos los strings
"España" # str normal — 'España' es perfectamente válido
len("España") # 6 — cuenta puntos de código, no bytes
len("España".encode("utf-8")) # 7 — 'ñ' ocupa 2 bytes en UTF-8
# Información de un carácter
ord("A") # 65 — punto de código Unicode
chr(65) # 'A' — carácter desde su código
ord("ñ") # 241
ord("🐍") # 128013
# unicodedata — información y normalización
unicodedata.name("ñ") # 'LATIN SMALL LETTER N WITH TILDE'
unicodedata.category("A") # 'Lu' — Letter, uppercase
unicodedata.category("1") # 'Nd' — Number, decimal digit
# Normalización — importante para comparaciones
# NFD: descompone "ñ" en "n" + combinador de tilde (2 code points)
# NFC: recompone en un único code point
unicodedata.normalize("NFD", "España") # 'España' (internamente diferente)
unicodedata.normalize("NFC", "España") # 'España' (recompuesta)
# Eliminar acentos (útil para slugs y búsqueda)
def quitar_acentos(texto):
nfd = unicodedata.normalize("NFD", texto)
return "".join(c for c in nfd if unicodedata.category(c) != "Mn")
quitar_acentos("Programación en España") # 'Programacion en Espana'
# Crear slugs para URLs
def crear_slug(texto):
sin_acentos = quitar_acentos(texto.lower())
return "".join(c if c.isalnum() else "-" for c in sin_acentos).strip("-")
crear_slug("Introducción a Python 3") # 'introduccion-a-python-3'
# Encode y decode
"hola".encode("utf-8") # b'hola'
"hola".encode("ascii") # b'hola'
"España".encode("utf-8") # b'Espa\xc3\xb1a'
"España".encode("latin-1") # b'Espa\xf1a'
# Decodificar bytes
b"Espa\xc3\xb1a".decode("utf-8") # 'España'
b"Espa\xf1a".decode("latin-1") # 'España'
# Manejo de errores en encode/decode
"España".encode("ascii", errors="ignore") # b'Espaa' — ignora no-ASCII
"España".encode("ascii", errors="replace") # b'Espa?a' — sustituye con ?
"España".encode("ascii", errors="xmlcharrefreplace") # b'España'
🛠️ Programa completo: analizador de texto
"""
Analizador de texto — aplica métodos de string para extraer estadísticas,
limpiar contenido y generar un informe formateado de un párrafo de texto.
"""
import unicodedata
import re
from collections import Counter
# ── 1. LIMPIEZA ──────────────────────────────────────────────────────────────
def limpiar_texto(texto):
"""Normaliza espacios, elimina caracteres de control y recorta."""
# Normalizar saltos de línea
texto = texto.replace("\r\n", "\n").replace("\r", "\n")
# Eliminar caracteres de control (excepto \n y \t)
texto = "".join(c for c in texto if c >= " " or c in "\n\t")
# Colapsar espacios múltiples en uno
lineas = [" ".join(linea.split()) for linea in texto.splitlines()]
return "\n".join(lineas).strip()
# ── 2. ESTADÍSTICAS ──────────────────────────────────────────────────────────
def analizar(texto):
"""Devuelve un dict con estadísticas completas del texto."""
texto_limpio = limpiar_texto(texto)
# Caracteres
total_chars = len(texto_limpio)
sin_espacios = len(texto_limpio.replace(" ", ""))
letras = sum(1 for c in texto_limpio if c.isalpha())
digitos = sum(1 for c in texto_limpio if c.isdigit())
# Líneas
lineas = texto_limpio.splitlines()
# Palabras
palabras = texto_limpio.split()
total_palabras = len(palabras)
palabras_lower = [p.lower().strip(".,;:!?\"'()[]") for p in palabras]
palabras_unicas = len(set(palabras_lower))
riqueza_lexica = palabras_unicas / total_palabras if total_palabras else 0
# Frases (separadas por . ? !)
frases = [f.strip() for f in re.split(r"[.!?]+", texto_limpio) if f.strip()]
longitud_media_frase = (
sum(len(f.split()) for f in frases) / len(frases) if frases else 0
)
# Palabras más frecuentes (excluyendo stopwords básicas)
STOPWORDS = {"el", "la", "los", "las", "un", "una", "y", "o", "de",
"del", "en", "a", "que", "es", "se", "no", "con", "por"}
palabras_contenido = [p for p in palabras_lower if p not in STOPWORDS and len(p) > 2]
top_palabras = Counter(palabras_contenido).most_common(5)
return {
"texto_limpio": texto_limpio,
"total_chars": total_chars,
"chars_sin_espacios": sin_espacios,
"letras": letras,
"digitos": digitos,
"total_lineas": len(lineas),
"total_palabras": total_palabras,
"palabras_unicas": palabras_unicas,
"riqueza_lexica": riqueza_lexica,
"total_frases": len(frases),
"longitud_media_frase": longitud_media_frase,
"top_palabras": top_palabras,
}
# ── 3. INFORME ────────────────────────────────────────────────────────────────
def imprimir_informe(stats):
"""Formatea y muestra el informe con f-strings y especificadores."""
sep = "═" * 52
print(f"\n {sep}")
print(f" {'ANÁLISIS DE TEXTO':^52}")
print(f" {sep}")
print(f"\n {'Caracteres':.<30} {stats['total_chars']:>10,}")
print(f" {'Sin espacios':.<30} {stats['chars_sin_espacios']:>10,}")
print(f" {'Letras':.<30} {stats['letras']:>10,}")
print(f" {'Dígitos':.<30} {stats['digitos']:>10,}")
print(f" {'Líneas':.<30} {stats['total_lineas']:>10,}")
print(f"\n {'Palabras totales':.<30} {stats['total_palabras']:>10,}")
print(f" {'Palabras únicas':.<30} {stats['palabras_unicas']:>10,}")
print(f" {'Riqueza léxica':.<30} {stats['riqueza_lexica']:>10.1%}")
print(f" {'Frases':.<30} {stats['total_frases']:>10,}")
print(f" {'Palabras por frase (media)':.<30} {stats['longitud_media_frase']:>10.1f}")
print(f"\n {'TOP 5 PALABRAS':}")
print(f" {'-'*30}")
for i, (palabra, freq) in enumerate(stats["top_palabras"], 1):
barra = "█" * freq
print(f" {i}. {palabra:<18} {freq:3} {barra}")
print(f"\n {sep}\n")
# ── 4. EJECUCIÓN ─────────────────────────────────────────────────────────────
TEXTO_EJEMPLO = """
Python es un lenguaje de programación de alto nivel, interpretado y de propósito general.
Python fue creado por Guido van Rossum y publicado por primera vez en 1991.
Python enfatiza la legibilidad del código y permite a los programadores expresar
conceptos en menos líneas de código que en C++ o Java.
Python es actualmente el lenguaje más popular del mundo según múltiples índices.
Python soporta múltiples paradigmas: programación orientada a objetos, programación
funcional y programación imperativa. Python cuenta con una comunidad enorme y un
ecosistema de bibliotecas que cubre desde ciencia de datos hasta desarrollo web.
"""
stats = analizar(TEXTO_EJEMPLO)
imprimir_informe(stats)
# Modo interactivo
print(" Pega tu texto (escribe FIN en una línea vacía para terminar):")
print(" " + "─" * 40)
lineas_usuario = []
while True:
linea = input(" ")
if linea.strip().upper() == "FIN":
break
lineas_usuario.append(linea)
if lineas_usuario:
texto_usuario = "\n".join(lineas_usuario)
stats_usuario = analizar(texto_usuario)
imprimir_informe(stats_usuario)
else:
print(" No se introdujo texto.")
🐛 Errores clásicos con strings
1. Concatenar string y número directamente
edad = 28
"Tengo " + edad + " años" # TypeError: can only concatenate str (not "int")
"Tengo " + str(edad) + " años" # ✅ convertir a string
f"Tengo {edad} años" # ✅ mejor: usar f-string
2. Esperar que replace() modifique el string in-place
s = "hola mundo"
s.replace("hola", "adiós") # no hace nada útil — el resultado se descarta
print(s) # sigue siendo 'hola mundo' ← bug silencioso
s = s.replace("hola", "adiós") # ✅ asignar el resultado
print(s) # 'adiós mundo'
3. Comparar strings con == ignorando mayúsculas o espacios
entrada = input("¿Continuar? (s/n): ")
if entrada == "s": # falla si el usuario escribe "S" o " s"
...
# ✅ Normalizar antes de comparar
if entrada.strip().lower() == "s":
...
4. Construir strings con + en un bucle (O(n²))
palabras = ["uno", "dos", "tres", "cuatro", "cinco"]
# ❌ Lento para listas grandes
resultado = ""
for p in palabras:
resultado += p + ", "
# ✅ Rápido: join
resultado = ", ".join(palabras)
5. Olvidar que split() sin argumento y split(" ") son diferentes
" hola mundo ".split() # ['hola', 'mundo'] ✅ limpio
" hola mundo ".split(" ") # ['', '', 'hola', '', '', 'mundo', '', ''] ← ojo
6. IndexError al acceder a un string vacío
s = ""
s[0] # IndexError: string index out of range
# ✅ Comprobar antes
if s:
primera = s[0]
# O con valor por defecto:
primera = s[0] if s else ""
✅ Resumen y próximos pasos
Los strings en Python combinan inmutabilidad, Unicode nativo y un arsenal de métodos que cubren prácticamente cualquier operación de texto: búsqueda, transformación, validación, división y formateo. Las f-strings hacen que el formateo sea tan natural como escribir la expresión directamente, con especificadores de formato que evitan conversiones manuales.
Con esta lección se completa el Módulo 3 — Estructuras de datos: listas, tuplas, diccionarios, conjuntos y ahora strings. El siguiente módulo, Funciones y módulos, comienza con def, parámetros y return, los bloques con los que encapsularás toda la lógica que acabas de aprender a manejar.
❓ Preguntas frecuentes
❓ Preguntas frecuentes sobre Strings en Python: métodos, f-strings y formateo de cadenas
Las dudas más comunes respondidas de forma clara y directa.
💬 Foro de discusión
¿Tienes dudas sobre Strings en Python: métodos, f-strings y formateo de cadenas? Comparte tu pregunta con la comunidad.
Todavía no hay mensajes. ¡Sé el primero en participar!