-->

Morse Code Converter in C

2020-08-09 04:22发布

问题:

Yes its homework We were suppose to have char 2D array holding character with size limit of 255 char char string[100][255];

Program neede: change the input morse code by a user to alphabet/English letter (Capital Letters)

Sample Input

2

 .... . .-.. .-.. --- / .-- --- .-. .-.. -..

 .--- --- -.- .

Sample OutPut

  • Case#1:

    HELLO WORLD
    
  • Case#2:

    JOKE
    

My only idea is to have the first characters of a word that is inputed by the user.. to be.. checked if its '.' or '-' then scrupulously and manually assigning.. the nested if(string[i][c]=='.') and the last if on each nested would be if(string[i][c]==' ') then that prints out the letter "E" example

if(string[i][c]=='.') {
    isspace(string[i][c+1])
    printf("E");
}

Now my question is.. is there any easier way for this problem? where i don't have to type the same '-' and '.' if statement.. and stuff? and have my mind explode? because I lose track of corresponding the next char with if or case statements?

回答1:

You have already discovered that you can branch on each morse signal and that it is tiresome to hard-code all that as if-else statements. When you have done so, you will have noted a certain structure with ever deeper nested conditions. You can represent this structure as a tree:

                       *
                   /       \
               E               T
             /   \           /   \
           I       A       N       M
          / \     / \     / \     / \ 
         S   U   R   W   D   K   G   O
        / \ / \ / \ / \ / \ / \ / \ / \ 
        H V F * L * P J B X C Y Z Q * *

That same tree can be found in a (slightly) prettier form in the middle sections of the Wikipedia entry on Morse code. (The asterisks in the lowest row indicate encodings that are not one of the 26 letters of the English alphabet.)

You start at the top. Branch left on a dit, branch right on a dah and read the value when you are done.

There are many ways to implement trees. In this case, the tree's branches are all of the same depth, at least if we consider the asterisks, too. You can represent the tree as a linear array by indexing the nodes row-wise. When toe top node is 1, you get:

                       1
                   /       \
               2               3
             /   \           /   \
           4       5       6       7
          / \     / \     / \     / \ 
         8   9  10  11  12  13  14  15
        / \ / \ / \ / \ / \ / \ / \ / \
       16 ...                     ... 31

You can see that branching left from node n brings you to node 2*n and branching right brings you to its right neighbour with index 2*n + 1. Build up the index as you go, starting from 1 and then look up your letter in the array:

const char *letter = "**ETIANMSURWDKGOHVF?L?PJBXCYZQ??";

(The two asterisks at the front indicate illegal indices.)



回答2:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>

static const char *alpha[] = {
    ".-",   //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
};
static const char *num[] = {
    "-----", //0
    ".----", //1
    "..---", //2
    "...--", //3
    "....-", //4
    ".....", //5
    "-....", //6
    "--...", //7
    "---..", //8
    "----.", //9
};
static const char **table[] = { alpha, num };

typedef enum kind {
    ALPHA, NUM
} Kind;

typedef struct mtree {
    char value;
    struct mtree *dot;
    struct mtree *bar;
} MTree;

MTree *root;

void make_tree(void);
void drop_tree(void);
void encode_out(const char *s);
void decode_out(const char *s);

int main(void){
    make_tree();
    encode_out("HELLO WORLD");
    encode_out("JOKE");
    decode_out(".... . .-.. .-.. --- / .-- --- .-. .-.. -..");
    decode_out(".--- --- -.- .");
    drop_tree();
    return 0;
}

void encode_out(const char *s){
    for(;;++s){
        char ch = *s;
        if(ch == '\0')
            break;
        if(isalpha(ch)){
            ch = toupper(ch);
            fputs(table[ALPHA][ch - 'A'], stdout);//`-'A'` depend on the sequence of character code
        } else if(isdigit(ch))
            fputs(table[NUM][ch - '0'], stdout);
        else if(ch == ' ')
            fputc('/', stdout);//need rest space skip ?
        else 
            ;//invalid character => ignore
        fputc(' ', stdout);
    }
    fputc('\n', stdout);
}
static void decode_out_aux(MTree *tree, const char *s){
    if(tree == NULL) return;
    if(*s == '\0')
        fputc(tree->value, stdout);
    else if(*s == '/')
        fputc(' ', stdout);
    else if(*s == '.')
        decode_out_aux(tree->dot, ++s);
    else if(*s == '-')
        decode_out_aux(tree->bar, ++s);
}
void decode_out(const char *s){
    char *p;
    while(*s){
        p = strchr(s, ' ');
        if(p){
            if(p-s != 0){
                char code[p-s+1];
                memcpy(code, s, p-s);
                code[p-s]='\0';
                decode_out_aux(root, code);
            }
            s = p + 1;
        } else {
            decode_out_aux(root, s);
            break;
        }
    }
    fputc('\n', stdout);
}
static void insert_aux(MTree **tree, char ch, const char *s){
    if(*tree == NULL)
        *tree = calloc(1, sizeof(**tree));
    if(*s == '\0')
        (*tree)->value = ch;
    else if(*s == '.')
        insert_aux(&(*tree)->dot, ch, ++s);
    else if(*s == '-')
        insert_aux(&(*tree)->bar, ch, ++s);
}

static inline void insert(char ch, const char *s){
    if(*s == '.')
        insert_aux(&root->dot, ch, ++s);
    else if(*s == '-')
        insert_aux(&root->bar, ch, ++s);
}

void make_tree(void){
    root = calloc(1, sizeof(*root));
    //root->value = '/';//anything
    int i;
    for(i = 0; i < 26; ++i)
        insert('A'+i, table[ALPHA][i]);
    for(i = 0; i < 10; ++i)
        insert('0'+i, table[NUM][i]);
}
static void drop_tree_aux(MTree *root){
    if(root){
        drop_tree_aux(root->dot);
        drop_tree_aux(root->bar);
        free(root);
    }
}
void drop_tree(void){
    drop_tree_aux(root);
}


回答3:

My very short and simple version::

#include <stdio.h>
#include <string.h>

typedef struct
{
    char* morse;
    char* ascii;
} morse_table_t;

int main(void) {
    char input[] = ".- -... -.-.";

    morse_table_t table[] = { {".-", "A"},
                              {"-...", "B"},
                              {"-.-.", "C"}
/* TODO: Fill in the rest of the Morse Table Here */
    };

    char* segment;
    int i;
    segment = strtok(input, " ");

    while(segment)
    {
        for(i=0; i<ARRAY_SIZE(table); ++i)
        {
            if (!strcmp(segment, table[i].morse)) puts(table[i].ascii);
        }
        segment = strtok(NULL, " ");
    }

    return 0;
}


回答4:

A brute force approach is the easiest -- not as brute as you propose, though.

  1. Create an array of input strings containing the Morse codes.
  2. Create an array of output strings containing what the strings from #1 represent (most of them will be a single character). Make sure this is in the exact same order as the array from #1. (You can do both at once using a 2-dimensional array or a structure, or possibly in even more advanced ways. Use what you know best.)
  3. Start of outer loop: initialize a destination string to empty.
  4. Read one character at a time from the input string, and:
    a. if it's a dash or dot, add it to the destination string;
    b. if not, end this loop.
  5. Repeat #4 until you encounter something not dash or dot. a. Compare the new string to each of the Morse codes in the array #1. When found, write the corresponding output code from the array #2.
    b. skip spaces in the input string;
    c. if you encounter the slash, write a space; and
    d. if you encounter the end of the input string, you are done.
  6. Repeat the loop at 3 until you encounter end-of-input.


回答5:

Here is a commented code that answers your question !

#include <stdio.h>
#include <string.h>

int main()
{
    /* string array will contain the whole line morse  code */
    char string[256]="";
    /* T is the number of test c ases */
    int T;
    scanf("%d ",&T);
    /* morse array contains all the letters from A to Z in  */
    /* morse code  */
    const char morse[][10]={
        ".-", //morse code of letter A
        "-...", //morse code of letter B
        "-.-." , //morse code of letter C
        "-.." , //morse code of letter D
        "." , //morse code of letter E
        "..-." , //morse code of letter F
        "--." , //morse code of letter G
        "...." , //morse code of letter H
        ".." , //morse code of letter I
        ".---" , //morse code of letter J
        "-.-" , //morse code of letter K
        ".-.." , //morse code of letter L
        "--" , //morse code of letter M
        "-." , //morse code of letter N
        "---" , //morse code of letter O
        ".--." , //morse code of letter P
        "--.-" , //morse code of letter Q
        ".-." , //morse code of letter R
        "..." , //morse code of letter S
        "-" , //morse code of letter T
        "..-" , //morse code of letter U
        "...-" , //morse code of letter V
        ".--" ,  //morse code of letter W
        "-..-" ,  //morse code of letter X
        "-.--" , //morse code of letter Y
        "--.." //morse code of letter Z
    };

    /* i will be used to print the number of test case */
    int i=1;
    /* while loop to do every  case */
    while(T--)
    {
        printf("Case#%d:\n",i);
        /* read the line of more code via f gets */
        fgets(string,sizeof(string),stdin);
        /* strtok is for extracting every word from the  line */
        char *p=strtok(string," ");
        while(p!=NULL)
        {
            /* check if the word is / print space and go to the next  word */
            if(p[0]=='/')
            {
                printf(" ");
                goto next;
            }
            int j=0;
            for(j=0; j<26;j++)
            {
                //check the correspondant string in morse array 
                    if(!strcmp(p,morse[j])) 
                    {
                        /* print the equivalent letter after finding the subscript */
                        /* A=65+0 .... Z=90=65+25 */
                        printf("%c",(j+65));
                    }
            }
next:
            /* fetch the next  word by ignoring space tab newline*/
            p=strtok(NULL,"\t \n");
        }
        printf("\n");
        i++;

    }

    return 0;
}

Keep in mind that this is not an optimal solution because for instance the search for the pattern is linear instead you can use the binary search after sorting the array !

Brievely, the code above can be improved !!

Hope it helps anyway !!



回答6:

#include<stdio.h>
#include<string.h>
#include<ctype.h>
#define MAX 100
#define SIZE 255

int main(){
char string[MAX][SIZE];
char destination[MAX][5];
char *input[37]={".-","-...","-.-.","-..",".","..-.","--.",
                "....","..",".---","-.-",".-..","--","-.",
                "---",".--.","--.-",".-.","...","-","..-",
                "...-",".--","-..-","-.--","--..","-----",
                ".----","..---","...--","....-",".....",
                "-....","--...","---..","----.","/"};
char *output[37]= {"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"," "};
int i, c, x, m, j;
printf("Enter the number of Cases:");
scanf("%d", &x);
for(i=0;i<x;i++){
    printf("Case#%d: ", i+1);
        if (i==0){
            gets(string[0]);    }
        gets(string[i]);
}

for(i=0;i<x;i++){
    printf("Case#%d: ",i+1);
    for(c=0,m=0;string[i][c]!='\0';c++,m++){
        if(isspace(string[i][c])){
            m++;} 
        else{
        destination[m][c]=string[i][c]; }
    }
    for(j=0,m=0;j<37;j++){
        if(destination[m]==input[j]){
            printf("%d %s \n", i+1, output[j]); m++;
        }
    }
 }  

 return 0;
 }
I might have done something stupid here... ._. i'm just trying though.. does this not work?


回答7:

sample of split string by using strtok

#include <stdio.h>
#include <string.h>

#define MAX 100
#define SIZE 255

int main(){
    char string[MAX][SIZE] = {
        ".... . .-.. .-.. --- / .-- --- .-. .-.. -..",
        ".--- --- -.- ."
    };
    char destination[MAX][8];
    int x = 2;//number of input
    int i, j, m;
    char *code, *separator = " ";//" " --> " \t\n"
    for(i=0;i<x;++i){
        j = 0;
        for(code = strtok(string[i], separator);
            code != NULL;
            code = strtok(NULL, separator)){
            printf("'%s'\n", code);
            strcpy(destination[j++], code);
        }
        m = j;
        if(strcmp(destination[0], "....")==0)
            puts("yes, It's 'H'.");
    }
    return 0;
}


回答8:

i found the solution! :D credits to BLUEPIXY for the for(i=0,j=0;i<x;++i){ for(code = strtok(string[i], separator);code != NULL;code = strtok(NULL,separator)){ strcpy(destination[i][j++], code);} }

Thanks guys

#include<stdio.h>
#include<string.h>
#define MAX 100
#define SIZE 255

int main(){
char string[MAX][SIZE];
char destination[MAX] [MAX][8];
char *input[38]={".-","-...","-.-.","-..",".","..-.","--.",
                "....","..",".---","-.-",".-..","--","-.",
                "---",".--.","--.-",".-.","...","-","..-",
                "...-",".--","-..-","-.--","--..","-----",
                ".----","..---","...--","....-",".....",
                "-....","--...","---..","----.","/"};
char *output[38]={"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"," "};
char *code, *separator = " ";
int i, c, x, j;
int m[MAX];
printf("Enter the number of Cases:");
scanf("%d", &x);
getchar();
for(i=0;i<x;i++){
    printf("Case#%d: ", i+1);
        gets(string[i]);
}

for(i=0,j=0;i<x;++i){
    for(code = strtok(string[i], separator);code != NULL;code = strtok(NULL, separator)){
        strcpy(destination[i][j++], code);

    }
    m[i] = j;
}

for(i=0;i<x;i++){
    printf("Case#%d: ", i+1);
    for(j=0;j<m[i];j++){
        for(c=0;c<37;c++){
            if(strcmp(destination[i][j], input[c])==0){
                printf("%s",output[c]);}
        }
    }
    printf("\n");
}
return 0;
}


回答9:

m-oehm's answer is really good, as I find all the other approaches involving tables a bit redundant. I followed the binary tree encoder, and came out with an example code that is ready to use.

The algorithm

You'll need to start with the binary tree parsed in preorder, find the index of the letter you want to encode to morse, convert that to binary, ignore the first digit, and then just assign the zeros to dots, and the ones to dashes. It's really simple.

A C implementation example

Also there is a full code example here.