Cómo funciona el código
El bucle recorre el texto carácter a carácter. ord() convierte una letra en su número: ord("A") es 65 y ord("a") es 97. Al restar esa base obtienes la posición de la letra en el alfabeto, de 0 a 25.
Después de sumar el desplazamiento, % 26 da la vuelta al alfabeto, de modo que con un desplazamiento de 3 la Z cae en la C. chr() devuelve el número a su forma de letra. Si el cifrado en sí es nuevo para ti, la guía lo explica sin código.
Espacios, signos y cifras pasan por la rama else y no cambian. Tampoco cambian la ñ ni las vocales con tilde, y para eso está isascii(): sin esa comprobación, Python trata la ñ como una minúscula normal, la convierte en otra letra cualquiera y el texto ya no se puede descifrar bien.
Descifrar en Python
No hace falta una segunda función. Descifrar es desplazar en sentido contrario, así que basta con pasar un número negativo:
print(caesar("¡Krod, pxqgr!", -3))# ¡Hola, mundo!Funciona porque aquí el % de Python nunca da un negativo: (0 - 3) % 26 da 23, y la A vuelve a la X. En C, Java y JavaScript saldría -3, por lo que la versión en JavaScript necesita un paso más.
Un programa que pide datos al usuario
Muchos ejercicios piden un programa que lea el texto y el desplazamiento por teclado. Añade esto debajo de la función:
text = input("Texto: ")shift = int(input("Desplazamiento: "))mode = input("¿Cifrar o descifrar? (c/d): ") if mode == "d": shift = -shift print(caesar(text, shift))input() siempre devuelve una cadena, por lo que el desplazamiento debe pasar por int(). Si alguien escribe una palabra en lugar de un número, Python se detiene con un ValueError.
Romper el cifrado por fuerza bruta
Con 26 letras solo hay 25 desplazamientos posibles, y un bucle corto basta para leer el mensaje sin la clave. Busca en la salida la línea que tenga sentido:
def crack(text): for shift in range(1, 26): print(shift, caesar(text, -shift)) crack("¡Krod, pxqgr!")# 1 ¡Jqnc, owpfq!# 2 ¡Ipmb, nvoep!# 3 ¡Hola, mundo!# ...El artículo sobre el ataque de fuerza bruta entra en más detalle, y el descifrador en línea hace lo mismo en tu navegador.
Una versión más corta con str.translate
Cuando entiendas el bucle, puedes cambiarlo por una tabla de traducción. str.maketrans() empareja cada letra con su versión desplazada, y translate() aplica la tabla a todo el texto. La ñ tampoco se toca aquí.
from string import ascii_lowercase as lower, ascii_uppercase as upper def caesar(text, shift): shift %= 26 table = str.maketrans( lower + upper, lower[shift:] + lower[:shift] + upper[shift:] + upper[:shift], ) return text.translate(table)Los resultados son iguales y, en textos largos, suele ir más rápido porque la tabla se crea una sola vez. Para un primer ejercicio, el bucle se explica mejor.
Cifrado César con la ñ
Para desplazar también la ñ, guarda el alfabeto en una cadena y busca cada carácter con index(). Son las mismas 27 letras que usa nuestra herramienta:
ALPHABET = "abcdefghijklmnñopqrstuvwxyz" def caesar_alphabet(text, shift): result = "" for char in text: letter = char.lower() if letter in ALPHABET: index = (ALPHABET.index(letter) + shift) % len(ALPHABET) new = ALPHABET[index] result += new.upper() if char.isupper() else new else: result += char return result print(caesar_alphabet("El niño", 3))# Hñ plqrAhora la vuelta depende de len(ALPHABET), así que no tienes que escribir el 27 en ningún sitio. Puedes comparar el resultado en la herramienta de cifrado César con el alfabeto español.
Cifrar un archivo de texto
Lee el archivo, pasa su contenido a la función y guarda el resultado en un archivo nuevo. Con encoding="utf-8", la ñ y las tildes se conservan en cualquier sistema.
with open("message.txt", encoding="utf-8") as source: text = source.read() with open("encrypted.txt", "w", encoding="utf-8") as target: target.write(caesar(text, 3))El mismo cifrado como clase
Una clase resulta útil si usas la misma clave muchas veces o si te piden código orientado a objetos. Guarda el desplazamiento y aprovecha la función del principio de la página:
class CaesarCipher: def __init__(self, shift=3): self.shift = shift def encrypt(self, text): return caesar(text, self.shift) def decrypt(self, text): return caesar(text, -self.shift) cipher = CaesarCipher(5)print(cipher.encrypt("¡Hola, mundo!"))# ¡Mtqf, rzsit!Probar el código
Unas pocas líneas con assert detectan casi todos los errores: pasar de la Z, conservar mayúsculas y minúsculas, respetar los signos y recuperar el texto original. Si todo va bien, el programa imprime OK.
assert caesar("ABC", 3) == "DEF"assert caesar("xyz", 3) == "abc"assert caesar("Hello, World!", 5) == "Mjqqt, Btwqi!"assert caesar(caesar("Python", 7), -7) == "Python"assert caesar("A", 26) == "A"assert caesar("é", 3) == "é"print("OK")Errores frecuentes
El clásico es escribir chr(ord(char) + shift) sin módulo, y así la Z con desplazamiento 3 se convierte en el símbolo ]. También es habitual usar la base 65 para todas las letras, lo que estropea las minúsculas. Y si olvidas int() alrededor de input(), Python se detiene con un TypeError al sumar el desplazamiento.
Preguntas frecuentes
¿Qué versión de Python necesito?
Python 3.7 o posterior, que es cuando se añadió isascii(). Hemos ejecutado el código de esta página en Python 3.9. No necesita paquetes adicionales.
¿Sirve para proteger datos reales?
No. El bucle de arriba lo rompe en segundos. Úsalo para aprender y para pasatiempos, y protege contraseñas o datos privados con cifrado moderno de una librería mantenida.
¿Cómo compruebo que mi resultado es correcto?
Introduce el mismo texto y desplazamiento en nuestra herramienta de cifrado César y compara. También puedes llamar a la API REST gratuita desde tus pruebas.