En este post vamos a implementar un traductor de código Morse en C. Veremos cómo codificar o transformar código Morse usando el lenguaje C.
Por ejemplo, haremos que la palabra HOLA
se convierta a su equivalente .... --- .-.. .-
y viceversa, logrando al final un traductor Morse.
Cada letra tiene un código que lo representa; yo me he basado en el siguiente archivo que contiene la equivalencia de cada letra con su respectivo código.
Signo,Código
A,.-
B,-...
C,-.-.
CH,----
D,-..
E,.
F,..-.
G,--.
H,....
I,..
J,.---
K,-.-
L,.-..
M,--
N,-.
Ñ,--.--
O,---
P,.--.
Q,--.-
R,.-.
S,...
T,-
U,..-
V,...-
W,.--
X,-..-
Y,-.--
Z,--..
0,-----
1,.----
2,..---
3,...--
4,....-
5,.....
6,-....
7,--...
8,---..
9,----.
.,.-.-.-
",",--..--
:,---...
?,..--..
',.----.
-,-....-
/,-..-.
comilla doble,.-..-.
@,.--.-.
=,-...-
!,−.−.−−
Esto es muy simple, solo hay que buscar la equivalencia de cada letra o de cada código Morse; para ello podemos tomar varios enfoques pero yo he elegido el de poner dos arreglos; uno con Morse y otro con ASCII, con cada valor equivalente en el mismo índice:
// Código y signo iguales deben estar en el mismo índice
char *codigosMorse[] = {
".-", "-...", "-.-.", "-..",
".", "..-.", "--.", "....", "..",
".---", "-.-", ".-..", "--", "-.",
"---", ".--.", "--.-", ".-.", "...",
"-", "..-", "...-", ".--", "-..-",
"-.--", "--..", "-----", ".----", "..---",
"...--", "....-", ".....", "-....", "--...",
"---..", "----.", ".-.-.-", "--..--", "---...",
"..--..", ".----.", "-....-", "-..-.", ".--.-.",
"-...-", ".-..-.", "-.-.--",};
char *simbolosAscii[] = {"A", "B", "C", "D",
"E", "F", "G", "H", "I",
"J", "K", "L", "M", "N",
"O", "P", "Q", "R", "S",
"T", "U", "V", "W", "X",
"Y", "Z", "0", "1", "2",
"3", "4", "5", "6", "7",
"8", "9", ".", ",", ":",
"?", "'", "-", "/", "@",
"=", "\"", "!",};
Por ejemplo, la letra A
equivale a .-
y el símbolo !
equivale a -.-.--
, están en distintos arreglos pero misma posición.
simbolosAscii
.codigosMorse
.Esto sería más fácil si en C existieran los diccionarios, pero no existen y su implementación es cuestión de otro post.
Nota: otro enfoque más óptimo (en cuanto a rendimiento, para no buscar el elemento por cada símbolo) sería usar múltiples sentencias if
o un diccionario.
Nota 2: si notas el algoritmo muy pesado, la verdad es que no lo es, lo pesado o complejo es trabajar con cadenas en C; si esto fuera otro lenguaje con mayor soporte de strings, nos llevaría menos de la mitad de código hacer el traductor de código Morse.
Necesitamos funciones para dividir el algoritmo y hacerlo más fácil de resolver. También viene bien saber la longitud del arreglo, la cual se calcula así:
const int NUMERO_CODIGOS = sizeof(codigosMorse) / sizeof(codigosMorse[0]);
Lo calculamos con cualquier arreglo porque se supone que ambos arreglos miden lo mismo.
Después necesitamos el índice de cada código Morse o cada código ASCII:
int buscarIndiceDeMorse(char *codigo) {
for (int x = 0; x < NUMERO_CODIGOS; x++) {
if (strcmp(codigosMorse[x], codigo) == 0) {
return x;
}
}
return -1;
}
int buscarIndiceDeAscii(char signo) {
// Convertir char a string
char cadenaTemporal[2];
cadenaTemporal[0] = signo;
cadenaTemporal[1] = '\0';
for (int x = 0; x < NUMERO_CODIGOS; x++) {
if (strcmp(simbolosAscii[x], cadenaTemporal) == 0) {
return x;
}
}
return -1;
}
Son funciones que simplemente devuelven el índice del símbolo o código.
En la función para buscar el índice del código ascii creamos una cadena temporal, o mejor dicho, lo que equivale a una cadena o string en C, esto es para que strcmp (que compara cadenas) funcione.
Ahora veamos la siguiente función que convierte un mensaje a su equivalente en Morse. Leemos el mensaje con fgets y luego simplemente imprimimos cada equivalencia:
void demostrarCodificacionMorse() {
// char mensaje[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789.,:?'-/@=\"!";
char mensaje[LONGITUD_MENSAJE];
printf("Mensaje a transformar en Morse: \n");
fgets(mensaje, LONGITUD_MENSAJE, stdin);
mensaje[strcspn(mensaje, "\r\n")] = 0; // Remover salto de línea
// Recorremos la cadena y transformamos cada letra
int i = 0;
while (mensaje[i]) {
// Convertir a mayúscula porque tenemos equivalencias solo para mayúsculas
// y Morse no especifica minúscula
char letraEnMayuscula = (char) toupper(mensaje[i]);
int indice = buscarIndiceDeAscii(letraEnMayuscula);
char *codigoMorse = codigosMorse[indice];
// Aquí puedes hacer lo que gustes con el morse, yo simplemente lo imprimo
printf("%s ", codigoMorse);
i++;
}
}
Estamos convirtiendo a mayúscula cada letra o símbolo pues en las equivalencias todo está en mayúscula. Cuando tenemos el código Morse lo imprimimos; como lo dije, puedes hacer cualquier cosa con él.
Como queremos que este sea un traductor de código Morse en C necesitamos también decodificar un mensaje en Morse.
Para esto, vamos a hacer que cada símbolo Morse esté separado por un espacio; luego usamos strtok (algo como el split
de C) y separamos la cadena por espacios.
void demostrarDecodificacionMorse() {
// char mensaje[] = ".- -... -.-. -.. . ..-. --. .... .. .--- -.- .-.. -- -. --- .--. --.- .-. ... - ..- ...- .-- -..- -.-- --.. ----- .---- ..--- ...-- ....- ..... -.... --... ---.. ----. .-.-.- --..-- ---... ..--.. .----. -....- -..-. .--.-. -...- .-..-. -.-.--";
// Aquí define tu mensaje separado por espacios
char mensaje[LONGITUD_MENSAJE];
printf("Mensaje Morse a decodificar:\n");
fgets(mensaje, LONGITUD_MENSAJE, stdin);
mensaje[strcspn(mensaje, "\r\n")] = 0; // Remover salto de línea
char delimitador[] = " ";
// Separamos la cadena por espacios. Más información sobre strtok en https://parzibyte.me/blog/2018/11/13/separar-cadena-delimitadores-c-strtok/
char *token = strtok(mensaje, delimitador);
if (token != NULL) {
while (token != NULL) {
// Obtener cuál índice tiene este código
int indice = buscarIndiceDeMorse(token);
// Aquí puedes hacer lo que gustes con el ascii, yo simplemente lo imprimo
char *ascii = simbolosAscii[indice];
printf("%s", ascii);
token = strtok(NULL, delimitador);
}
}
}
Ahora hacemos casi lo mismo, solo que agregamos la implementación para separar la cadena. Después obtenemos el índice con buscarIndiceDeMorse
y lo usamos para acceder a simbolosAscii
; al resultado final (ascii
) lo imprimimos con printf
.
Ahora veamos el código completo; ya lo he explicado por partes. Todo está dividido en funciones que se invocan desde otros métodos o desde la función main
.
#include <stdio.h> // Para printf
#include <string.h> // Para strcmp y strlen
#include <ctype.h> // Para toupper
#define LONGITUD_MENSAJE 500
/**
* Traductor de código Morse en C
*
* Basado en https://parzibyte.me/blog/2019/10/20/traductor-codigo-morse-c/
*
* @author parzibyte
* https://parzibyte.me/blog
*
* */// Código y signo iguales deben estar en el mismo índice
char *codigosMorse[] = {
".-", "-...", "-.-.", "-..",
".", "..-.", "--.", "....", "..",
".---", "-.-", ".-..", "--", "-.",
"---", ".--.", "--.-", ".-.", "...",
"-", "..-", "...-", ".--", "-..-",
"-.--", "--..", "-----", ".----", "..---",
"...--", "....-", ".....", "-....", "--...",
"---..", "----.", ".-.-.-", "--..--", "---...",
"..--..", ".----.", "-....-", "-..-.", ".--.-.",
"-...-", ".-..-.", "-.-.--",};
char *simbolosAscii[] = {"A", "B", "C", "D",
"E", "F", "G", "H", "I",
"J", "K", "L", "M", "N",
"O", "P", "Q", "R", "S",
"T", "U", "V", "W", "X",
"Y", "Z", "0", "1", "2",
"3", "4", "5", "6", "7",
"8", "9", ".", ",", ":",
"?", "'", "-", "/", "@",
"=", "\"", "!",};
const int NUMERO_CODIGOS = sizeof(codigosMorse) / sizeof(codigosMorse[0]);
int buscarIndiceDeMorse(char *codigo) {
for (int x = 0; x < NUMERO_CODIGOS; x++) {
if (strcmp(codigosMorse[x], codigo) == 0) {
return x;
}
}
return -1;
}
int buscarIndiceDeAscii(char signo) {
// Convertir char a string
char cadenaTemporal[2];
cadenaTemporal[0] = signo;
cadenaTemporal[1] = '\0';
for (int x = 0; x < NUMERO_CODIGOS; x++) {
if (strcmp(simbolosAscii[x], cadenaTemporal) == 0) {
return x;
}
}
return -1;
}
void demostrarCodificacionMorse() {
// char mensaje[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789.,:?'-/@=\"!";
char mensaje[LONGITUD_MENSAJE];
printf("Mensaje a transformar en Morse: \n");
fgets(mensaje, LONGITUD_MENSAJE, stdin);
mensaje[strcspn(mensaje, "\r\n")] = 0; // Remover salto de línea
// Recorremos la cadena y transformamos cada letra
int i = 0;
while (mensaje[i]) {
// Convertir a mayúscula porque tenemos equivalencias solo para mayúsculas
// y Morse no especifica minúscula
char letraEnMayuscula = (char) toupper(mensaje[i]);
int indice = buscarIndiceDeAscii(letraEnMayuscula);
char *codigoMorse = codigosMorse[indice];
// Aquí puedes hacer lo que gustes con el morse, yo simplemente lo imprimo
printf("%s ", codigoMorse);
i++;
}
}
void demostrarDecodificacionMorse() {
// char mensaje[] = ".- -... -.-. -.. . ..-. --. .... .. .--- -.- .-.. -- -. --- .--. --.- .-. ... - ..- ...- .-- -..- -.-- --.. ----- .---- ..--- ...-- ....- ..... -.... --... ---.. ----. .-.-.- --..-- ---... ..--.. .----. -....- -..-. .--.-. -...- .-..-. -.-.--";
// Aquí define tu mensaje separado por espacios
char mensaje[LONGITUD_MENSAJE];
printf("Mensaje Morse a decodificar:\n");
fgets(mensaje, LONGITUD_MENSAJE, stdin);
mensaje[strcspn(mensaje, "\r\n")] = 0; // Remover salto de línea
char delimitador[] = " ";
// Separamos la cadena por espacios. Más información sobre strtok en https://parzibyte.me/blog/2018/11/13/separar-cadena-delimitadores-c-strtok/
char *token = strtok(mensaje, delimitador);
if (token != NULL) {
while (token != NULL) {
// Obtener cuál índice tiene este código
int indice = buscarIndiceDeMorse(token);
// Aquí puedes hacer lo que gustes con el ascii, yo simplemente lo imprimo
char *ascii = simbolosAscii[indice];
printf("%s", ascii);
token = strtok(NULL, delimitador);
}
}
}
int main() {
demostrarCodificacionMorse();
printf("\n");
demostrarDecodificacionMorse();
return 0;
}
En mi caso lo he probado así:
Recuerda que uso fgets
porque es más seguro que scanf. Si quieres que esto funcione con cadenas más largas simplemente cambia el límite en LONGITUD_MENSAJE
.
Por cierto, no estoy manejando el caso de que la función para buscar índices regrese -1
; si quieres hacer el algoritmo más a prueba de errores simplemente haz esa comprobación.
Hoy te voy a presentar un creador de credenciales que acabo de programar y que…
Ya te enseñé cómo convertir una aplicación web de Vue 3 en una PWA. Al…
En este artículo voy a documentar la arquitectura que yo utilizo al trabajar con WebAssembly…
En un artículo anterior te enseñé a crear un PWA. Al final, cualquier aplicación que…
Al usar Comlink para trabajar con los workers usando JavaScript me han aparecido algunos errores…
En este artículo te voy a enseñar cómo usar un "top level await" esperando a…
Esta web usa cookies.