Basado en el proyecto de la Raspberripy Org "Codebreakers".
¿Qué es lo que haremos en este post?
Cifrando el alfabeto.
#Estructura de datos
#Lista creada desde una cadena de texto
ALFABETO = list(" abcdefghijklmnñopqrstuvwxyz ")
codigo = {}
Creando una nueva lista con el alfabeto invertido.
# Crear el código Atbash invirtiendo el alfabeto def crear_codigo(): inversa = list(reversed(ALFABETO))
Codificando el alfabeto.
"Codificar" es cuando conviertes datos de un formato a otro. Ahora tienes dos listas. Una contiene la lista del alfabeto en su orden normal y la otra en su orden inverso. Vamos a usar ambas para rellenar un diccionario. La llave almacenará almacenará el alfabeto en su orden normal y el valor correspondiente guardará el diccionario al revés.
El diccionario codigo es muy importante porque lo usaremos para hacer coincidir cada letra del mensaje usando las llaves y sus correspondientes valores.
Dentro de la función crear_codigo, rellena el diccionario usando los datos de ambas listas. Usa un bucle "for" para obtener la longitud de la lista del alfabeto y completar el diccionario con los datos.
La función len() se usa para obtener la longitud de un objeto, como puede ser una lista. Aquí se usar para que el bucle "for" itere tantas veces como elementos tiene la lista "ALFABETO", es decir su longitud.
def crear_codigo(): # reversa del ALFABETO inversa = list(reversed(ALFABETO)) for i in range(len(ALFABETO)): # Longitud de una lista codigo[ALFABETO[i]] = inversa[i] # Rellena el alfabeto con una letra y su correspondiente # letra códificada.
Ahora tenemos un problema a solventar. En el idioma español existen los acentos. Para usarlos en nuestro código tenemos que pasar las letras acentuadas como letras normales ya que si no no se procesarían al no estar en el diccionario. Para solventarlo añadimos el diccionario caracteres_especiales y luego añadimos sus elementos al diccionario codigo.
# Crear el código Atbash invirtiendo el alfabeto def crear_codigo(): # reversa del ALFABETO inversa = list(reversed(ALFABETO)) for i in range(len(ALFABETO)): # Longitud de una lista codigo[ALFABETO[i]] = inversa[i] # Rellena el alfabeto con una letra y su correspondiente # letra códificada. # Lista de caracteres especiales y sus equivalentes en el alfabeto normal caracteres_especiales = {'á': 'a', 'é': 'e', 'í': 'i', 'ó': 'o', 'ú': 'u'} # Agregar caracteres especiales al diccionario de códigos codigo.update(caracteres_especiales)
Crear una función de entrada "main.py" es útil para llamar a las funciones que se necesitan cuando comienza el programa. Creemos una función main.py que llame a la función crear_codigo.
# Comienzo def main(): crear_codigo() main()
Probar y depurar el código.
# Crear el código Atbash invirtiendo el alfabeto def crear_codigo(): # reversa del ALFABETO inversa = list(reversed(ALFABETO)) for i in range(len(ALFABETO)): # Longitud de una lista codigo[ALFABETO[i]] = inversa[i] # Rellena el alfabeto con una letra y su correspondiente # letra códificada. # Lista de caracteres especiales y sus equivalentes en el alfabeto normal caracteres_especiales = {'á': 'a', 'é': 'e', 'í': 'i', 'ó': 'o', 'ú': 'u'} # Agregar caracteres especiales al diccionario de códigos codigo.update(caracteres_especiales) print(codigo) #Estructura de datos #Lista creada desde una cadena de texto ALFABETO = list("abcdefghijklmnñopqrstuvwxyz") codigo = {} # Comienzo def main(): crear_codigo() main()
{' ': ' ', 'a': 'z', 'b': 'y', 'c': 'x', 'd': 'w', 'e': 'v', 'f': 'u', 'g': 't', 'h': 's', 'i': 'r', 'j': 'q', 'k': 'p', 'l': 'o', 'm': 'ñ', 'n': 'n', 'ñ': 'm', 'o': 'l', 'p': 'k', 'q': 'j', 'r': 'i', 's': 'h', 't': 'g', 'u': 'f', 'v': 'e', 'w': 'd', 'x': 'c', 'y': 'b', 'z': 'a', 'á': 'a', 'é': 'e', 'í': 'i', 'ó': 'o', 'ú': 'u'}
Codificar un mensaje.
def crear_codigo(): # reversa del ALFABETO inversa = list(reversed(ALFABETO)) for i in range(len(ALFABETO)): # Longitud de una lista codigo[ALFABETO[i]] = inversa[i] # Rellena el alfabeto con una letra y su correspondiente # letra códificada. # Lista de caracteres especiales y sus equivalentes en el alfabeto normal caracteres_especiales = {'á': 'a', 'é': 'e', 'í': 'i', 'ó': 'o', 'ú': 'u'} # Agregar caracteres especiales al diccionario de códigos codigo.update(caracteres_especiales) #print(codigo)
def atbash(texto):
def atbash(texto):
"""Codifica o decodifica una línea de texto.
Atbash es símetrico.
"""
texto = texto.lower() # Convierte el texto en minúsculas.
salida = ""
def atbash(texto):
"""Codifica o decodifica una línea de texto.
Atbash es símetrico.
"""
texto = texto.lower() # Convierte el texto en minúsculas.
salida = ""
for letra in texto:
if letra in codigo:
salida += codigo[letra]
return salida
>>> %Run main.py
xlwrurxzi vh uaxro
Creando un menú.
def menu(): """Crea un menú basado en texto."""
def menu():
"""Crea un menú basado en texto."""
eleccion = "" # El bucle comienza con una elección no válida.
Puedes usar un bucle while para ejecutar un trozo de código mientras una condición se verdadera (True). En nuestro caso vamos a definir que mientras que el usuario no elija la letra "d" o la "f" el bucle se continuará ejecutando. Lo que pretendemos es que si se pulsa una "c" se codifique/decodifique un mensaje que habremos guardado en un archivo de texto y si pulsa una "f" se muestre un gráfico con la frecuencia de las letras en ese mensaje.
def menu(): """Crea un menú basado en texto.""" eleccion = "" while elección != 'c' and eleccion != 'f': eleccion = input('''Por favor, introduce c to codificar/decodificar texto, o f para realizar un análisis de frecuencias: ''')
Una vez que el usuario ha introducido una elección correcta, el bucle finaliza. A continuación crearemos una sentencia 'if' que ejecutará la función atbash si el usuario introduce una 'c'.
Tu decidirás que sucederá cuando el usuario introduzca una 'f' en un paso posterior.
Debajo de la última línea (¡asegurándote de tener todavía una sangría!), escribe:
def menu():
"""Crea un menú basado en texto."""
eleccion = ""
while eleccion != 'c' and eleccion != 'f':
eleccion = input('''Por favor, introduce c to codificar/decodificar texto,
o f para realizar un análisis de frecuencias: ''')
if eleccion == 'c':
print("Pasando tu mensaje a través del cifrado...")
mensaje = "Mi mensaje secreto"
codigo = atbash(mensaje)
print(codigo)
Cambia la cadena que dice "Mi mensaje secreto" por el texto que tu quieras. Este texto será el que se codifique o decodifique.
Al final de la función main() teclea menu() para llamar a la función cuando se ejecute el programa:
# Comienzo def main(): crear_codigo() #print(atbash("Codificar es fácil")) menu()
>>> %Run main.py
Por favor, introduce c to codificar/decodificar texto,
o f para realizar un análisis de frecuencias:
c
Pasando tu mensaje a través del cifrado...
ñr ñvnhzqv hvxivgl
Codificar texto desde un archivo.
def obtener_texto(nombre_archivo): """Obtiene y devuelve texto desde un archivo.""" with open(nombre_archivo) as f: texto = f.read().replace('\n', " ") # tenemos que eliminar los saltos de línea. return texto
def menu():
"""Crea un menú basado en texto."""
eleccion = ""
while eleccion != 'c' and eleccion != 'f':
eleccion = input('''Por favor, introduce c to codificar/decodificar texto,
o f para realizar un análisis de frecuencias: \n''')
if eleccion == 'c':
print("Pasando tu mensaje a través del cifrado...")
mensaje = obtener_texto('datos.txt')
codigo = atbash(mensaje)
print(codigo)
>>> %Run main.py Por favor, introduce c para codificar/decodificar texto, o f para realizar un análisis de frecuencias: c Pasando tu mensaje a través del cifrado... vhgv vh fn ñvnhzqv wv kifvyz kziz evi hr vo xlwrurxzwli wv gvcglh ufnxrlnz xliivxgzñvngv
Decodificando el mensaje.
>>> %Run main.py Por favor, introduce c para codificar/decodificar texto, o f para realizar un análisis de frecuencias: c Pasando tu mensaje a través del cifrado... este es un mensaje de prueba para ver si el codificador de textos funciona correctamente
Crear un analizador de frecuencias.
def frecuencia(texto): """Calcula la frecuencia de las letras en un texto""" texto = list(texto.lower())
def frecuencia(texto):
"""Calcula la frecuencia de las letras en un texto"""
texto = list(texto.lower())
frec = {}
for letra in texto:
frec[letra] = frec.get(letra, 0) + 1
def frecuencia(texto): """Calcula la frecuencia de las letras en un texto""" texto = list(texto.lower()) frec = {} for letra in texto: if letra != " ": frec[letra] = frec.get(letra, 0) + 1 letras_totales = sum(frec.values()) #numero total de letras sin espacios en blanco for letra in frec: frec[letra] = frec[letra] / letras_totales * 100 # Convierte números en porcentajes. return frec
Extendiendo el menú para incluir la opción 'f'.
def menu():
"""Crea un menú basado en texto."""
eleccion = ""
while eleccion != 'c' and eleccion != 'f':
eleccion = input('''Por favor, introduce c para codificar/decodificar texto,
o f para realizar un análisis de frecuencias: \n''')
if eleccion == 'c':
print("Pasando tu mensaje a través del cifrado...")
# mensaje = "Mi mensaje secreto"
mensaje = obtener_texto('datos.txt')
codigo = atbash(mensaje)
print(codigo)
elif eleccion == 'f':
mensaje = obtener_texto('datos.txt')
codigo = atbash(mensaje)
mensaje_frec = frecuencia(codigo)
print(mensaje_frec)
>>> %Run main.py Por favor, introduce c para codificar/decodificar texto, o f para realizar un análisis de frecuencias: f {'v': 18.91891891891892, 'h': 6.756756756756757, 'g': 6.756756756756757, 'f': 4.054054054054054, 'n': 6.756756756756757, 'ñ': 2.7027027027027026, 'z': 9.45945945945946, 'q': 1.3513513513513513, 'w': 5.405405405405405, 'k': 2.7027027027027026, 'i': 8.108108108108109, 'y': 1.3513513513513513, 'e': 1.3513513513513513, 'r': 5.405405405405405, 'o': 1.3513513513513513, 'x': 6.756756756756757, 'l': 6.756756756756757, 'u': 2.7027027027027026, 'c': 1.3513513513513513}
Analizando la frecuencia mediante un gráfico.
En el menú no necesitaremos más la línea print(mensaje_frec) así que lo comentaremos para que se ignore cuando ejecutemos el código.
elif eleccion == 'f':
mensaje = obtener_texto('datos.txt')
codigo = atbash(mensaje)
mensaje_frec = frecuencia(codigo)
# print(mensaje_frec)
Crear el gráfico con las frecuencias.
Vamos a definir una nueva función llamada crear_grafico(). Esta función tendrá dos parámetros, texto y lenguaje. El gráfico será un gráfico de barras que tendrá por título "análisis de frecuencias" y las etiquetas para el eje-x serán las claves del diccionario. (a,b,c...y,z)
El diccionario de frecuencias lo pasaremos posteriormente a la función cuando se la llame a través del parámetro texto.
Primeramente después de haberlo instalado importamos al principio del programa el tipo de gráfico que vamos a crear.
from pygal import Bar #... def crear_grafico(texto, lenguaje): """Crea el gráfico de frecuencias""" grafico = Bar(width=800, height=400, title="Análisis de Frecuencias", x_labels=list(lenguaje.keys()))
Etiquetaremos el gráfico usando la frecuencia con la que las letras aparecen en el mensaje codificado y las frecuencias conocidas de las letras en el idioma español. Estos datos los pasaremos a través de los parámetros texto y lenguaje.
Para pasar la frecuencia con la que las letras aparecen en un texto genérico en el idioma español, crearemos una variable llamada "spanish" y a través de un diccionario pasaremos las frecuencias.
Tenemos que solucionar un problema. A partir de la versión 3.7 de Python los datos en un diccionario se guardan en el orden en el que se han introducido y aunque no fuera así para poder representar correctamente el gráfico tenemos que emparejar las frecuencias de las letras en el mensaje codificado, con las frecuencias de las letras en el idioma. Es decir si la primera letra de nuestro texto codificado es una 'e' y las segunda una "s" pues tendremos que ordenar todas alfabéticamente y hacer coincidir sus valores con los del diccionario del lenguaje, que están ordenados alfabéticamente.
Para ello creamos una lista llamada "valores" que recogerá las frecuencias de las letras del mensaje codificado pero ordenadas alfabéticamente desde la 'a' a la 'z'. A través de un bucle for recorremos las letras "a,b,c...z" y si la letra existe pasamos su valor a la lista y sino pues sustituimos su valor con un cero.
def crear_grafico(texto, lenguaje): """Crea el gráfico de frecuencias""" grafico = Bar(width=800, height=400, title="Análisis de Frecuencias", x_labels=list(lenguaje.keys())) # Etiquetas de las frecuencias para el mensaje codificado. valores = [] for i in lenguaje: valores.append(texto.get(i,0)) grafico.add('Mensaje objetivo', valores) # Etiquetas de las frecuencias de las letras en el idioma español grafico.add('Lenguaje', list(lenguaje.values())) grafico.render_to_file('grafico.svg') # Estructura de datos # Lista creada desde una cadena de texto ALFABETO = list(" abcdefghijklmnñopqrstuvwxyz ") codigo = {} spanish = {'a': 12.53, 'b': 1.42, 'c': 4.68, 'd': 5.86, 'e': 13.68, 'f': 0.69, 'g': 1.01, 'h': 0.70, 'i': 6.25, 'j': 0.44, 'k': 0.02, 'l': 4.97, 'm': 3.15, 'n': 6.71, 'ñ': 0.31, 'o': 8.68, 'p': 2.51, 'q': 0.88, 'r': 6.97, 's': 7.98, 't': 4.63, 'u': 3.93, 'v': 0.90, 'w': 0.01, 'x': 0.22, 'y': 0.90, 'z': 0.52}
LLamando a la función que representa las frecuencias.
Busca la secuencia elif dentro de la función menu(). Añade una línea de código para utilizar las frecuencias del idioma español y otra para crear el gráfico.
elif eleccion == 'f': mensaje = obtener_texto('datos.txt') codigo = atbash(mensaje) mensaje_frec = frecuencia(codigo) # print(mensaje_frec) lang_freq = spanish # Importamos la frecuencia de la letras en español # LLama a la función que crea el gráfico crear_grafico(mensaje_frec, lang_freq)
Ejecuta el código y puedes ver el archivo del gráfico usando cualquier navegador para abrirlo:
Analiza el gráfico de frecuencias.
El gráfico que se ha producido muestra la frecuencia de letras en el idioma español, etiquetado como "Lenguaje". Puedes ver que la letra "e" es la letra más utilizada en el idioma español porque tiene la barra más alta para todos los valores de idioma.
El gráfico de frecuencias también lista la frecuencia de letras en tu mensaje codificado, etiquetado como "Mensaje objetivo". Para averiguar qué codificación se ha utilizado para este mensaje, puedes comparar las barras que muestran el idioma español con las barras en el mensaje codificado. La barra más alta en el texto del mensaje codificado probablemente será una "e". La segunda letra más alta probablemente será una "a", ya que es la siguiente letra más popular.
Los descifradores pueden utilizar la frecuencia de letras para determinar el tipo de codificación que se ha utilizado en el mensaje. Pueden utilizar el método de ensayo y error para predecir qué letra podría representar usando el gráfico como guía.
No obstante ten en cuenta que tu mensaje es muy pequeño lo que dificulta el descifrarlo mediante el análisis de frecuencias. Sin embargo todo se normaliza si codificas un texto lo suficientemente largo.
Puedes encontrar el código de este post en este enlace de Github.
No hay comentarios:
Publicar un comentario