Fundamentos
Strings a fondo en Python: rebanado y métodos más usados
Domina los strings en Python: índices y rebanadas, inmutabilidad, split, join, replace, find, formato, y lo que el MicroPython de la ESP32 no tiene.
Por Prof. Rodrigo Faustino · Actualizado el · 15 min de lectura
Métricas de este artículo
- Palabras
- 2945
- Caracteres
- 16.759
- Tiempo de lectura
- ~15 min
Un string, o texto, es una secuencia de caracteres. Como la lista y la tupla, tiene índice, rebanada y len, y se recorre con for. A diferencia de la lista, no puede alterarse en el lugar: todo método de texto devuelve un texto nuevo y deja el original como estaba. La mayor parte del trabajo con strings consiste en saber qué métodos existen, qué devuelve cada uno y cómo combinar unos pocos para resolver problemas reales, como limpiar lo que el usuario escribió, separar los campos de un mensaje o alinear números en una pantalla.
Los artículos anteriores ya usaron el texto de varias formas: print e input, la conversión con str() e int(), strip e isdigit para validar, las f-strings. Aquí el tema gana su propio espacio. Veremos los índices y las rebanadas, la inmutabilidad, los métodos más usados en grupos, el formato, los caracteres y las secuencias de escape, algunas recetas, usos en la ESP32, y una parte importante: varios métodos de texto del Python de computador no existen en MicroPython, y el simulador, que lo imita, reproduce esa falta.
El texto es una secuencia
texto = "Python"
print(texto[0], texto[1], texto[-1], texto[-2])
print(texto[1:4], texto[:2], texto[3:], texto[:])
print(texto[2:100], repr(texto[4:1]))
print(len(texto), texto + "!", texto * 2)
print("th" in texto, "ht" in texto, "Py" in texto)
for letra in texto:
print(letra, end="-")
print()
print(ord("A"), ord("a"), chr(66), chr(97))
print("a" < "b", "B" < "a", "casa" < "casaco", "10" < "9")
print("fin")La salida es P y n o, yth Py hon Python, thon '', 6 Python! PythonPython, True False True, P-y-t-h-o-n-, 65 97 B a, True True True True y fin.
Todo lo que el artículo sobre listas dijo sobre índices y rebanadas vale aquí: el primer carácter está en el índice 0, el -1 es el último, texto[1:4] va del índice 1 hasta antes del 4, y una rebanada fuera de los límites no da error. La diferencia está en el resultado: la rebanada de un texto es otro texto, y la de una lista es otra lista.
El operador in aplicado a un texto busca un pedazo, y no solo un carácter. "th" in "Python" da True, y "ht" da False, porque el orden importa. El + junta textos, y el * repite.
Las dos últimas líneas recuerdan que el computador guarda cada carácter como un número. El ord("A") devuelve 65, y el chr(66) devuelve B. La comparación entre textos usa esos números, carácter a carácter, y por eso "B" < "a" es verdadero, ya que las mayúsculas vienen antes que las minúsculas en la tabla. Y "10" < "9" también, porque el "1" viene antes que el "9": comparar números escritos como texto da orden alfabético, y no numérico. Es el mismo cuidado que el artículo sobre conversión de tipos pidió al leer datos del teclado.
La rebanada con paso no funciona en el simulador
En el Python de computador, texto[::-1] es la receta más conocida para invertir un texto. En el simulador, que imita a MicroPython, la rebanada de texto con paso distinto de 1 no existe, como ya ocurría con la tupla:
texto = "Python"
print(texto[::-1])NotImplementedError: only slices with step=1 (aka None) are supportedLa solución es pedirle al reversed, que recorre de atrás hacia adelante, y unir el resultado:
texto = "Python"
print("".join(reversed(texto)))
print("".join(reversed("abc")))
print(list(texto)[::-1])
print("".join(list(texto)[::-1]))
print(texto[::1])
print(texto[0:6:1])
print("fin")La salida es nohtyP, cba, ['n', 'o', 'h', 't', 'y', 'P'], nohtyP, Python, Python y fin. El "".join(reversed(texto)) es el camino más corto. La lista acepta la rebanada con paso, y por eso list(texto)[::-1] funciona, pero la rebanada directa en el texto, solo con el paso 1.
Del programa al semestre planificado
- Plan de enseñanza y calendario montados solos
- Diario de clases listo para pegar en el SIGA
- Diapositivas y listas de ejercicios con solucionario
Versión Community gratis · Windows
El texto no cambia: los métodos devuelven otro
Intentar cambiar una letra da error, y los métodos de transformación, como upper, no alteran la variable:
s = "Ana"
print(s.upper(), s)
t = s.upper()
print(t, s)
print("a" + "b" * 3, "ab" "cd")
print("=" * 10)
print("Python".lower().startswith("py"), "x".join("abc"))
print("fin")La salida es ANA Ana, ANA Ana, abbb abcd, una línea de diez signos de igual, True axbxc y fin. El s.upper() entregó ANA, y la variable s siguió siendo Ana. Para guardar el resultado hay que asignar, como en t = s.upper(), o s = s.upper() si el original ya no hace falta. Los métodos se encadenan: "Python".lower().startswith("py") primero baja las letras y después pregunta por el comienzo. El "x".join("abc") usa la x como separador entre las letras de abc, y el "ab" "cd" muestra que dos textos escritos lado a lado se juntan solos.
Los métodos más usados
Los métodos que existen en el simulador caben en cuatro familias. La figura trae un ejemplo de cada uno, y el ejemplo de abajo ejecuta los principales:
s = " Oye, Mundo "
print(repr(s.strip()), repr(s.lstrip()), repr(s.rstrip()))
t = "Oye, Mundo"
print(t.upper(), t.lower())
print(t.replace("o", "0"), t.replace("o", "0", 1))
print(t.find("o"), t.find("z"), t.rfind("o"), t.count("o"))
print(t.index("M"))
print(t.startswith("Oye"), t.endswith("do"), t.startswith("oye"))
print(t.split(","), t.split())
print("a;b;c".split(";"), "a;b;c".split(";", 1))
print("-".join(["a", "b", "c"]), "".join(["x", "y"]), ", ".join(["1", "2"]))
print("fin")La salida es 'Oye, Mundo' 'Oye, Mundo ' ' Oye, Mundo', después OYE, MUNDO oye, mundo, Oye, Mund0 Oye, Mund0, 9 -1 9 1, 5, True True False, ['Oye', ' Mundo'] ['Oye,', 'Mundo'], ['a', 'b', 'c'] ['a', 'b;c'], a-b-c xy 1, 2 y fin.
Limpiar y reemplazar. El strip quita los espacios de las dos puntas, el lstrip solo los de la izquierda y el rstrip solo los de la derecha. El repr se usó para mostrar los espacios que sobran. El upper y el lower cambian entre mayúsculas y minúsculas. El replace(antiguo, nuevo) reemplaza todas las apariciones, y un tercer argumento limita cuántas.
Buscar. El find devuelve la posición de la primera aparición, o -1 si no hay ninguna, y el rfind busca desde el final. El index hace lo mismo que el find, pero da error si no encuentra. El count cuenta las apariciones, sin superponerlas. Fíjate en que el t.count("o") dio 1, y no 2: la O mayúscula de Oye no es una o, y la búsqueda distingue mayúsculas de minúsculas. El startswith y el endswith preguntan por el comienzo y por el final.
Dividir y unir. El split(sep) corta el texto en una lista. Sin argumento, corta en cualquier secuencia de espacios, y por eso t.split() dio ['Oye,', 'Mundo']. Con un separador, como ",", corta exactamente ahí y conserva los espacios, y el segundo elemento quedó ' Mundo', con el espacio delante. Un segundo argumento limita el número de cortes. El join hace lo contrario: el separador va antes del punto, y la lista, entre paréntesis: "-".join(lista). Los elementos de la lista deben ser textos.
Dos más que caben aquí, y la lista de preguntas sobre el contenido:
print("123".isdigit(), "12a".isdigit(), "abc".isalpha(), "ab1".isalpha())
print(" ".isspace(), "ABC".isupper(), "abc".islower(), "Abc".isupper(), "Abc".islower())
print("ab".center(6) + "|")
print("a,b,c".partition(","), "a,b,c".rpartition(","))
print("a b c d".rsplit(" ", 1), "a b c d".split(" ", 1))
print("línea1\nlínea2".splitlines())
print("banana".rfind("an"), "banana".find("an"), "banana".rindex("a"))
print("fin")La salida es True False True False, True True True False False, ab |, ('a', ',', 'b,c') ('a,b', ',', 'c'), ['a b c', 'd'] ['a', 'b c d'], ['línea1', 'línea2'], 3 1 5 y fin.
Preguntar. El isdigit, el isalpha, el isspace, el isupper y el islower dan True o False. Atención: son verdaderos solo cuando todos los caracteres pasan, y el texto vacío no pasa. "Abc".isupper() y "Abc".islower() dan False los dos, porque el texto tiene las dos formas. El partition(sep) corta una vez y devuelve tres partes en una tupla: antes, el separador y después, y el rpartition corta en la última aparición. El splitlines separa por saltos de línea. El center(6) centra el texto en un ancho, y aquí, en el simulador, acepta solo el ancho, sin el carácter de relleno.
Lo que el computador tiene y la placa no
Si aprendiste métodos de texto en un curso de Python para computador, vas a toparte con una sorpresa: varios métodos que parecen básicos no existen en MicroPython. En el simulador, los siguientes dan AttributeError: capitalize, title, swapcase, casefold, ljust, rjust, zfill, isalnum, isidentifier, istitle, removeprefix, removesuffix, translate y format_map, entre otros. Todos existen en el Python de computador. La figura muestra qué escribir en lugar de los más usados, y el ejemplo de abajo lo confirma:
frase = "orden de los factores"
palavras = frase.split()
print(len(palavras), palavras)
print([p[0].upper() + p[1:] for p in palavras])
print(" ".join(p[0].upper() + p[1:] for p in palavras))
print("".join(p[0] for p in palavras))
s = "7"
print("0" * (3 - len(s)) + s, f"{7:03d}")
nome = "ana"
print(nome[0].upper() + nome[1:])
print(f"{'ab':>5}|{'ab':<5}|{'ab':^5}|")
def so_letras_e_digitos(t):
return all(c.isalpha() or c.isdigit() for c in t)
print(so_letras_e_digitos("ab1"), so_letras_e_digitos("ab 1"))
print("fin")La salida es 4 ['orden', 'de', 'los', 'factores'], ['Orden', 'De', 'Los', 'Factores'], Orden De Los Factores, odlf, 007 007, Ana, ab|ab | ab |, True False y fin.
El sustituto de la primera letra mayúscula es t[0].upper() + t[1:]. Para el title, se junta la misma cuenta en cada palabra del split. Para alinear a la izquierda, a la derecha o al centro, la f-string lo resuelve con :<, :> y :^. Para completar con ceros a la izquierda, el :03d hace el trabajo de zfill en números, y "0" * (3 - len(s)) + s lo hace en textos. Lo que preguntaría el isalnum se vuelve un all sobre los caracteres.
La regla de oro es probar el método en el simulador antes de usarlo en un programa para la placa. Si aparece AttributeError: 'str' object has no attribute ..., el método no existe, y la lista de arriba da el camino.
La IA que recuerda tu vida y vive en tu ordenador
- Memoria a largo plazo con la fuente de cada respuesta
- Funciona sin conexión: tus datos se quedan en tu PC
- Lee PDF, diapositivas, fotos y grabaciones de reuniones
Plan Esencial gratis para siempre
Dar formato a los textos
Las f-strings ya se presentaron en el artículo sobre print e input. Aquí, el resumen de las opciones de formato, y las dos formas más antiguas, que todavía aparecen en código por ahí:
nome = "Ana"
idade = 20
nota = 8.5
print(f"{nome} tiene {idade} años")
print(f"{nota:.2f} | {idade:5d}| {idade:<5d}| {idade:05d}")
print(f"{nome:>6}|{nome:<6}|{nome:^7}|")
print("{} tiene {} años".format(nome, idade))
print("{0} y {1} y {0}".format("a", "b"))
print("%s tiene %d años" % (nome, idade))
print("%.1f%%" % 45.5)
print(f"{255:x} {255:X} {5:08b}")
print(f"{'aspas'} {nome!r}")
print("fin")La salida es Ana tiene 20 años, 8.50 | 20| 20 | 00020, Ana|Ana | Ana |, Ana tiene 20 años, a y b y a, Ana tiene 20 años, 45.5%, ff FF 00000101, aspas 'Ana' y fin.
Después de los dos puntos viene la especificación: .2f da dos decimales, 5d reserva cinco posiciones para un entero, <, > y ^ alinean a la izquierda, a la derecha y al centro, 05d completa con ceros, y x, X y b escriben en hexadecimal y en binario. El !r muestra el repr, con las comillas. El método format usa {} en el texto y recibe los valores entre paréntesis, con o sin números de posición, y el operador % usa %s, %d y %f. Las tres formas dan el mismo resultado, y la f-string es la más legible. Si usas :d con un texto, el error es ValueError, como se ve más adelante.
Caracteres especiales, comillas y acentos
print("línea1\nlínea2")
print("columna1\tcolumna2")
print("barra \\ y comillas \" y '")
print('comillas "dobles" dentro')
print("""varias
líneas""")
print(r"c:\nuevo")
print(len("niño"), "niño"[2], "niño".upper())
print("ç" < "d", ord("ç"))
print(len("a\nb"))
print("fin")La salida es línea1 y línea2 en dos líneas, columna1 y columna2 separadas por tabulación, barra \ y comillas " y ', comillas "dobles" dentro, varias y líneas en dos líneas, c:\nuevo, 4 ñ NIñO, False 231, 3 y fin.
La barra invertida inicia una secuencia de escape: \n es el salto de línea, \t es la tabulación, \\ es una barra y \" es una comilla dentro de un texto delimitado por comillas. El texto entre comillas simples puede tener comillas dobles dentro, y al revés. Las comillas triples guardan el texto en varias líneas. El prefijo r (de raw, crudo) desactiva los escapes, y es útil para rutas de Windows como c:\nuevo, en que el \n no es un salto de línea. El texto "a\nb" tiene 3 caracteres, porque el salto cuenta como uno.
Con acentos, el simulador cuenta los caracteres de la manera esperada: len("niño") da 4, y "niño"[2] es la ñ. Pero hay una trampa en las dos últimas cuentas: "niño".upper() devolvió NIñO, con la ñ intacta. En MicroPython, upper y lower solo cambian las letras sin acento. En el Python de computador, el resultado sería NIÑO. La comparación "ç" < "d" también dio False, porque el código de ç es 231, bastante mayor que el de d, y por eso ordenar palabras acentuadas por la comparación directa no sigue el orden alfabético del español. Lo mismo ocurre con la ñ, que en el alfabeto va entre la n y la o, pero en la tabla de caracteres está mucho más adelante. Si el programa necesita ignorar mayúsculas en un texto con acento, trata las letras acentuadas a mano, o evita depender de eso.
Recetas con texto
Algunos problemas clásicos muestran los métodos trabajando juntos:
def eh_palindromo(t):
t = t.lower().replace(" ", "")
return t == "".join(reversed(t))
print(eh_palindromo("Ana"), eh_palindromo("Python"), eh_palindromo("Amo la paloma"))
def cesar(t, k):
r = ""
for c in t:
if "a" <= c <= "z":
r += chr((ord(c) - ord("a") + k) % 26 + ord("a"))
else:
r += c
return r
print(cesar("abc xyz", 3), cesar(cesar("abc xyz", 3), -3))
vogais = 0
for c in "programacion":
if c in "aeiou":
vogais += 1
print(vogais)
print("fin")La salida es True False True, def abc abc xyz y 5, seguidos de fin. El palíndromo (texto que se lee igual en los dos sentidos) baja las letras, quita los espacios y compara el texto con su inverso, y la inversión usa el join con reversed que vimos. El cifrado César desplaza cada letra k posiciones en el alfabeto: ord(c) - ord("a") da la posición de 0 a 25, la suma con k y el % 26 dan la vuelta al alfabeto, y el chr convierte de vuelta. Los caracteres fuera de a a z, como el espacio, pasan sin cambiar, y aplicar el cifrado con -3 deshace el de 3. El contador de vocales usa el in para probar si la letra está en "aeiou". Atención: el ejemplo del cifrado y el de las vocales solo tratan letras minúsculas sin acento.
En la ESP32: mensajes, comandos y columnas
En proyectos con la placa, el texto llega de afuera en formatos simples: una línea con campos separados, un comando escrito, una respuesta de sensor. El split y el startswith resuelven la mayoría:
mensagem = "temp=24;umid=61;modo=auto"
dados = {}
for par in mensagem.split(";"):
chave, valor = par.split("=")
dados[chave] = valor
print(sorted(dados.items()))
temp = int(dados["temp"])
print(temp + 1)
comando = "LED ON"
if comando.startswith("LED"):
estado = comando.split()[1]
print("estado", estado)
nomes = ["Temp", "Hum"]
valores = [24, 61]
for nome, v in zip(nomes, valores):
print(f"{nome:<6}{v:>4}")
print(f"{24:>3}C {61:>3}%")
print("fin")La salida es [('modo', 'auto'), ('temp', '24'), ('umid', '61')], 25, estado ON, Temp 24, Hum 61, 24C 61% y fin. El primer bloque rompe el mensaje en pares con split(";"), abre cada par en chave y valor con split("="), y guarda en un diccionario, como presentó el artículo sobre diccionarios. Los valores siguen siendo textos, y el int(...) convierte lo que sea número. El sorted en la impresión evita depender del orden del diccionario. El segundo bloque lee un comando: startswith("LED") reconoce la familia y split()[1] toma la palabra siguiente. El tercero arma columnas alineadas con la f-string, lo que es útil en una pantalla como la del artículo sobre I2C con OLED y LCD, en que cada línea tiene un ancho fijo.
Errores comunes
Los mensajes de abajo son los del simulador, que imita a MicroPython. El tipo del error suele ser el mismo en el Python de computador, y el texto puede cambiar.
s = "Python"
s[0] = "J"TypeError: 'str' object doesn't support item assignmentEl texto no acepta el cambio de una letra. La forma es armar otro: "J" + s[1:].
s = "Python"
print(s[6])IndexError: string index out of rangeEl texto tiene 6 caracteres, y los índices van de 0 a 5. Es el mismo error del lista[len(lista)].
print("edad: " + 20)TypeError: unsupported types for __add__: 'str', 'int'El + entre texto y número no es una cuenta válida. Usa str(20), la coma del print o una f-string.
print("abc".index("z"))ValueError: substring not foundEl index da error cuando el pedazo no existe. El find devuelve -1, y es la elección cuando la ausencia es normal.
print("a,b".split(",")[2])IndexError: list index out of rangeEl split devolvió una lista de dos elementos, y el índice 2 no existe. El error aparece en la lista, y no en el texto.
print(",".join([1, 2, 3]))TypeError: join expects a list of str/bytes objects consistent with self objectEl join solo une textos. Convierte antes: ",".join([str(n) for n in lista]).
x, y = "a=b=c".split("=")ValueError: too many values to unpack (expected 2)El split encontró dos signos de igual y devolvió tres partes. Para cortar solo en el primero, usa split("=", 1).
print(f"{'x':d}")ValueError: unknown format code 'd' for object of type 'str'El formato d es para enteros. En un texto, el formato correcto es el s, o ninguno.
print("abc"[1.5])TypeError: string indices must be integers, not floatEl índice debe ser entero, como en la lista. La causa suele ser una división con /, y la corrección, el //.
Lo que suele aparecer en el aula
Estos patrones se repiten en grupos de todos los niveles.
El primero es escribir s.upper() solo en una línea y creer que s cambió. No da error, y la línea no hace nada: el resultado se descarta. La otra variante es s.strip() antes de un if, y después probar la variable original. Cada vez que un método de texto "no funciona", la primera pregunta es si el resultado se guardó.
El segundo es el + con número, visto arriba, que sigue siendo el error más frecuente de quien acaba de aprender el input.
El tercero es la comparación de números escritos como texto. "10" < "9" da True, y sorted(["10", "9", "100"]) ordena como un diccionario, y no como número. La cura es convertir con int antes de comparar.
El cuarto es el split(",") en datos que tienen espacios, que deja los espacios pegados a los elementos, y el programa deja de reconocer "b" porque el elemento es " b". El strip en cada elemento lo resuelve.
El quinto es buscar con in y después usar el index sin recordar que el texto puede aparecer más de una vez, o con otras mayúsculas. "Python" y "python" son textos distintos, y la búsqueda los distingue.
El sexto es llevar los métodos del computador a la placa. El programa corre en el PC, y en el simulador o en la ESP32 el title o el zfill dan AttributeError. Es el tipo de error que solo aparece a la hora de grabar.
El séptimo es la rebanada [::-1], que funciona en el PC y no funciona en la placa, y es la razón por la que existe la sección de las rebanadas.
Ejercicio: analizar un correo electrónico
Escribe un programa que lea un correo electrónico, quite los espacios de las puntas, pase todo a minúsculas y muestre el usuario, el dominio, la extensión (la última parte del dominio) y las iniciales de las partes del usuario separadas por punto. Si el texto no tiene @, debe avisar que es inválido.
email = input("correo: ").strip().lower()
if "@" not in email:
print("inválido")
else:
usuario, dominio = email.split("@")
print("usuario", usuario)
print("dominio", dominio)
print("extensión", dominio.split(".")[-1])
print("iniciales", "".join(p[0].upper() for p in usuario.split(".")))Con la entrada Ana.Silva@Escuela.com.es, las cuatro líneas finales son usuario ana.silva, dominio escuela.com.es, extensión es e iniciales AS. El split("@") separa las dos partes, y el desempaquetado del artículo sobre tuplas las entrega en usuario y dominio. El split(".")[-1] toma la última parte del dominio, y la comprensión toma la primera letra de cada parte del usuario. Para practicar, trata el caso de un correo con dos @, que hoy rompe el desempaquetado, y verifica si la extensión tiene al menos dos caracteres.
Preguntas frecuentes
¿Puedo alterar una letra de un texto? No. Arma otro texto con rebanadas, como "J" + s[1:], o convierte a lista con list(s), modifica y une de vuelta con "".join(...).
¿Cuál es la diferencia entre find e index? El find devuelve -1 cuando no encuentra, y el index da ValueError. Elige según lo que signifique la ausencia en tu programa.
¿Comillas simples o dobles? Da igual, y el resultado es el mismo. Usa las que evitan escapar: "it's" o 'dice "hola"'.
¿Cómo comparo textos sin distinguir mayúsculas? Pasa los dos a lower() antes de comparar. Con letras acentuadas, recuerda la limitación de upper y lower en MicroPython.
¿Cómo junto números y textos en una sola línea? Con f-string, que convierte sola: f"{nome} tiene {idade} años".
¿El split() sin argumento es igual al split(" ")? No. El que no tiene argumento trata cualquier secuencia de espacios como un solo separador e ignora los de las puntas. El split(" ") corta en cada espacio, y dos espacios seguidos generan un elemento vacío.
Para continuar
Los desafíos Longitud del nombre, Repetir texto y Comparar textos practican el len, la repetición y la comparación de textos. Para repasar las bases, los artículos sobre print e input, conversión de tipos y diccionarios completan este.
El próximo artículo trata de las matrices: listas de listas para guardar tablas con filas y columnas, cómo recorrerlas con bucles anidados y dónde aparecen en proyectos con la placa.