Morse Code

Pages: 12
In my desire to improve at programming I wrote the following code to convert english to morse and morse to english.

I am currently a new student, only three class into the term, this site has helped me immensely.

This code was written on a Mac using Xcode.

No questions on the code, just looking for feedback... Thanks

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
//
//  main.cpp
//  Morse Code
//
//  Special characters are not handled.
//  Only letters and numbers are handled.
//
//  Created with Xcode
//

#include <iostream>
#include <string>
#include <sstream>
#include <vector>
#include <iomanip>  //for setw

//function prototypes
void displaymorse(struct language key[], int SIZE);
void englishtomorse(struct language key[], int SIZE);
void morsetoenglish(struct language key[], int SIZE);
std::vector<std::string> &split(const std::string &s, char delim, std::vector<std::string> &elems);
std::vector<std::string> split(const std::string &s, char delim);

struct language
{
    std::string english;
    std::string morse;
};

int main()
{
    const int SIZE = 37;
    int choice=1;

    language key[SIZE];
    key[0] = {"a", ".-"};
    key[1] = {"b", "-..."};
    key[2] = {"c", "-.-."};
    key[3] = {"d", "-.."};
    key[4] = {"e", "."};
    key[5] = {"f", "..-."};
    key[6] = {"g", "--."};
    key[7] = {"h", "...."};
    key[8] = {"i", ".."};
    key[9] = {"j", ".---"};
    key[10] = {"k", "-.-"};
    key[11] = {"l", ".-.."};
    key[12] = {"m", "--"};
    key[13] = {"n", "-."};
    key[14] = {"o", "---"};
    key[15] = {"p", ".--."};
    key[16] = {"q", "--.-"};
    key[17] = {"r", ".-."};
    key[18] = {"s", "..."};
    key[19] = {"t", "-"};
    key[20] = {"u", "..-"};
    key[21] = {"v", "...-"};
    key[22] = {"w", ".--"};
    key[23] = {"x", "-..-"};
    key[24] = {"y", "-.--"};
    key[25] = {"z", "--.."};
    key[26] = {"0", "-----"};
    key[27] = {"1", ".----"};
    key[28] = {"2", "..---"};
    key[29] = {"3", "...--"};
    key[30] = {"4", "....-"};
    key[31] = {"5", "....."};
    key[32] = {"6", "-...."};
    key[33] = {"7", "--..."};
    key[34] = {"8", "---.."};
    key[35] = {"9", "----."};
    key[36] = {" ", " "};
    
    std::cout << "This programm will convert a letter, number, word or phrase\n";
    std::cout << "from english to morse code, or from morse code to english.\n";
    std::cout << "Only letters and numbers are converted, others are ignored,\n";
    std::cout << "all letters are converted to lowercase automatically.\n";
    std::cout << "\n";
    
    while (choice != 0)
    {
        std::cout << "What would you like to do?\n";
        std::cout << "Enter 0 to exit...\n";
        std::cout << "Enter 1 to display the morse code table...\n";
        std::cout << "Enter 2 for english to morse code...\n";
        std::cout << "Enter 3 for morse code to english...\n";
        std::cout << "Enter your choice: ";

        std::cin >> choice;

        //if user entered something besides a number we handle it here
        //after clearing the error we set choice to 4 to inform the user
        //of an invalid entry and ask them to try again
        if(!std::cin)
        {
            std::cin.clear();
            std::cin.ignore(999,'\n');
            choice = 4;
        }
            
        std::cout << "\n";

        switch (choice)
        {
            case 0:
                std::cout << "\nGood bye\n\n\n";
                exit(0);
                break;
            case 1:
                displaymorse(key, SIZE);
                break;
            case 2:
                englishtomorse(key, SIZE);
                break;
            case 3:
                displaymorse(key, SIZE);
                morsetoenglish(key, SIZE);
                break;
            default:
                std::cout << "\nInvalid choice, please try again.\n\n\n";
                break;
        }
    }
    return 0;
}

void displaymorse(struct language key[], int SIZE)
{
    const int width = 6;
    //display the table in 3 columns so we divide the size by 3
    for (int i=0; i<(SIZE-1)/3; i++)//minus 1 so we dont include the space
    {
        std::cout << key[i].english << " = " << std::left << std::setw(width) << (key[i].morse) << "\t"; //left justify
        std::cout << key[i+SIZE/3].english << " = " << std::setw(width) << (key[i+SIZE/3].morse) << "\t";
        std::cout << key[i+SIZE/3+SIZE/3].english << " = " << std::setw(width) << (key[i+SIZE/3+SIZE/3].morse) << "\n";

    }
    std::cout << std::right << "\n";    //return back to default right justify
}

void englishtomorse(struct language key[], int SIZE)
{
    std::string character = "";
    std::string input = "";
    std::string word = "";
    std::vector<std::string> words;
    std::vector<std::string> ingnoreditem;
    bool found=false;

    std::cout << "Enter a letter, number, word, or phrase to convert to morse code: ";
    std::cin.ignore();//clear any newlines cin may have left behind
    getline(std::cin, input, '\n');

    std::cout << "Desired input for translation: \"" << input << "\"\n";
    std::cout << "Morse code output: ";

    std::istringstream iss(input);
    while (iss)
    {
        iss >> word;
        for(unsigned int j = 0; j<word.size(); j++)
        {
            character = word[j];// so we are comparing a string to a string
            for(int i=0; i<SIZE; i++)
            {
                if(key[i].english == character)
                {
                    std::cout << key[i].morse << " ";
                    found = true;
                }
            }
            if (found == false)
            {
                ingnoreditem.push_back(character);
            }
            found = false;//set it back to false
        }
        std::cout << " ";//add another space so between words is double spaced
        word="";//clear word
    }
    if(ingnoreditem.size() != 0)
    {
        std::cout << "\nThe following items were ignored: ";
        for(unsigned int i = 0; i<ingnoreditem.size(); i++)
        {
            std::cout << "\"" << ingnoreditem[i] << "\" ";
        }
    }

    std::cout << "\n\n\n";
}

void morsetoenglish(struct language key[], int SIZE)
{
    std::string input = "";
    std::string morsechrs = "";
    std::vector<std::string> morse;
    std::vector<std::string> ingnoreditem;
    bool found=false;
    
    std::cout << "Enter morse code, seperate letters with a single space\n";
    std::cout << "seperate words with a double space: ";
    std::cin.ignore();//clear any newlines cin may have left behind
    getline(std::cin, input);

    std::cout << "Morse code entered: \"" << input << "\"\n";
    std::cout << "Translates to: ";
    
    morse = split(input, ' ');
    for(unsigned int i = 0; i<morse.size(); i++)
    {
        if(morse[i] == "")
        {
            std::cout << " ";
        }
        for(int j=0; j<SIZE; j++)
        {
            if(key[j].morse == morse[i])
            {
                std::cout << key[j].english;
                found = true;
            }
        }
        if (found == false)
        {
            if(morse[i] != "")
            {
                ingnoreditem.push_back(morse[i]);
            }
        }
        found = false;//set it back to false
    }
    if(ingnoreditem.size() != 0)
    {
        std::cout << "\nThe following items were ignored: ";
        for(unsigned int i = 0; i<ingnoreditem.size(); i++)
        {
            std::cout << "\"" << ingnoreditem[i] << "\" ";
        }
    }
    std::cout << "\n\n\n";
}

std::vector<std::string> &split(const std::string &s, char delim, std::vector<std::string> &elems)
{
    std::stringstream ss(s);
    std::string item;
    while (std::getline(ss, item, delim)) {
        elems.push_back(item);
    }
    return elems;
}

std::vector<std::string> split(const std::string &s, char delim)
{
    std::vector<std::string> elems;
    split(s, delim, elems);
    return elems;
}
Last edited on
I think this is a good program for a beginner. My main concern is your spacing.

Now code formatting can start "holy wars" but I only want to point out that when operators are not spaced-out, the code is hard for me to read. Compare this:

1
2
3

std::cout << key[i+SIZE/3].english << " = " << std::setw(width) << (key[i+SIZE/3].morse) << "\t";


With this:

1
2
3
std::cout << key[ i + SIZE / 3 ].english << " = " << std::setw( width ) <<
    ( key[ i + SIZE / 3 ].morse ) << "\t";


Of course, this is only my opinion: YMMV.

Great job, otherwise!
Love the idea for the program. It ran fine on my compiler and seemed to work very well.

I'm not sure if this works on Xcode, but on my compiler you can type "using namespace std;" at the very beginning so you don't have to type "std::" everytime, so:

1
2
using namespace std;
cout << "Hello World!" << endl;


works the same as

std::cout << "Hello World!" << endl;

I'm not sure if this works on Xcode, but on my compiler you can type "using namespace std;" at the very beginning so you don't have to type "std::" everytime, so:

That is true, however you really should start getting used to using the scope resolution operator:: instead of the using statements. The using namespace std; statement was originally designed to make porting pre-standard C++ programs to standard C++ programs easier. Today using the using statements are considered poor practice in most circumstances.

Let me also add that it also ran fine on my computer. JLB is right (and he seems to be right all the time!). He is among several very good programmers who are here to help us all.

thanks everyone
closed account (48T7M4Gy)
Goes well. As a suggestion and not a criticism this project lends itself to STL containers particularly <maps> with 2 maps, one keyed on alpha character and one keyed on on the morse code as a string is something to consider for the future.
I will look into STL containers and maps
kemort
So would I need a map for english to morse and another map for morse to english???

From what I have read, you access the data based on the key, but can you access the key based on the data?

1
2
3
4
5
6
7
8
    std::map <char, std::string> morseList;
    morseList['a'] = ".-";
    morseList['b'] = "-...";
    morseList['c'] = "-.-.";
    morseList['d'] = "-..";
    morseList['e'] = ".";
    morseList['f'] = "..-.";
    morseList['g'] = "--.";
closed account (48T7M4Gy)
1
2
3
4
5
6
7
8
9
10
11
12
13
    std::map<char,  std::string> alpha_to_morse;
    std::map<char,  std::string>::const_iterator it_a_m;
    char alpha;
    
    std::map< std::string, char> morse_to_alpha;
    std::map< std::string, char>::const_iterator it_m_a;
    std::string morse;
    
    while (file >> alpha >> morse)
    {
            alpha_to_morse.insert( std::pair<char,  std::string>(alpha, morse) );
            morse_to_alpha.insert( std::pair< std::string, char>(morse, alpha) );
    }


Code input file "morse.dat":

a .-
b -...
c -.-.
d -.. etc


If you prepare a text file with the code table you can read in the file and prepare the two maps required simultaneously like the above code snippet. The two maps enable you to access the info in both directions. There are libraries around that enable you to prepare a single bi-directional map but why bother when this is simple and straightforward.

You'll see you can save a lot of coding.

Good luck with it.
Last edited on
can you access the key based on the data?

No. You would need a map for each direction.

Just a suggestion, but I would not hard code the values for the second map. I would create the second map from the first.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include <map>
#include <string>
using namespace std;

map <char, string> morseList;
map <string, char> asciiList;

void load_morse ()
{   morseList['a'] = ".-";
    morseList['b'] = "-...";
    morseList['c'] = "-.-.";
// etc 
}
    
void load_ascii_from_morse ()
{   map<char,string>::const_iterator citer;

    for (citer=morseList.begin(); citer != morseList.end(); citer++)
    {    pair<string,char>  pr (citer->second, citer->first);
         asciiList.insert (pr);
    }
}



Last edited on
Looks like I have more reading to do, I've never worked with reading in files yet...
closed account (48T7M4Gy)
This tutorial here talks about files:
http://www.cplusplus.com/doc/tutorial/files/

There is quite an advantage in using a data file rather than hard coding, especially if you need to modify or add special characters, assuming that is allowed in Morse code. Also your data file doesn't have any punctuation, brackets, quote marks etc etc. It also makes life easier just to edit the code rather than having slabs of 'junk' getting in the way.
Last edited on
closed account (48T7M4Gy)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
    std::map<char,  std::string> alpha_to_morse;
    std::map<char,  std::string>::const_iterator it_a_m;
    char alpha;

    std::map< std::string, char> morse_to_alpha;
    std::map< std::string, char>::const_iterator it_m_a;
    std::string morse;

    std::ifstream file("morse.dat");
    if(file.is_open())
    {
        while (file >> alpha >> morse)
        {
            alpha_to_morse.insert( std::pair<char,  std::string>(alpha, morse) );
            morse_to_alpha.insert( std::pair< std::string, char>(morse, alpha) );
        }
        
       // Menu and options go here.
    else
        std::cout << "Unable to open file.\n";
    }
Last edited on
AbstractionAnon

could you please explain whats going on in this function. I understand what it does, I'm just not grasping how it does it.

1
2
3
4
5
6
7
8
void load_ascii_from_morse ()
{   map<char,string>::const_iterator citer;

    for (citer=morseList.begin(); citer != morseList.end(); citer++)
    {    pair<string,char>  pr (citer->second, citer->first);
         asciiList.insert (pr);
    }
}


thanks
Line 2: This is an iterator that we're going to use to iterate through morseList.

Line 4: A standard iterator based for loop. We start by using begin() to set citer to the first element of morseList. We continue as long as the iterator is not equal to end(), which points to the element past the last element in morseList. Each time through the for loop, citer is incremented to point to the next element in morseList.

Line 5: map::insert requires a std::pair, which is the key (morse string) and value (letter) we want to insert into asciiList. Note that for asciiList, the types of the key and value are reversed from morseList, so we reverse the key and value from citer. citer->first is the letter and citer->second is the morse string.

Line 6: We simply insert the morse,char pair we built into asciiList.

Clear?

Last edited on
that does make more sense... what does the pr do?

is it holding the pair from the first map and being inserted into the newly created map.
Last edited on
is it holding the pair from the first map and being inserted into the newly created map.

Almost. pr is an object of type pair<string,char>. It's holding a newly constructed pair of the morse string and char to be inserted into asciiList.
that makes sense... thanks

I'm going to do a rewrite to incorporate this.

Gotta finish my homework for my composition class first though... YUCK! (where's a MR Yuck sticker when you need one?)
Last edited on
closed account (37oyvCM9)
#cool
Pages: 12