Convert to and from Morse Code in C#
Table of Contents
- Introduction
- Requirements
- Morse Code dictionary
- Converting String to Morse Code requirements
- Converting Morse Code to String requirements
- Testing
- Valid C# String to Morse Code test cases
- Invalid C# String to Morse Code test cases
- Valid Morse Code to C# String test cases
- Invalid Morse Code to C# String test cases
- Converting the test cases to C#
- Implementing the C# String to Morse Code converter
- Defining the map between C# characters and Morse Code strings
- Define character and word constants
- Implementing the Morse Code to C# String parser
- Summary
- Full implementation code
- C# String to Morse Code Converter
- Morse Code to C# String parser
Introduction
Converting text to Morse code and back again is one of those simple exercises that sharpens your thinking around dictionaries, string manipulation, and control flow. It also gives you a taste of how encoding systems work behind the scenes.
In this article, we will walk through how to create a Morse Code converter from scratch in C#, exploring both the encoding and decoding logic, and showing how even a lightweight exercise like this can deepen your understanding of the C# language. Above all, it is fun fun fun!
Requirements
As mentioned in Parsing Integers Manually article, having strong requirements defind up front can save you time and effort later, and avoid costly refactoring. Therefore we need to come up with the requirements for converting C# strings to and from Morse code, whilst accounting for edge cases and quirks.
Morse Code dictionary
Before we start, we need to know how to represent text as Morse Code, and also what characters are supported by the morse code alphabet. Below is a dictionary of symbols that make up each valid alphabetical letter and number in Morse Code.
String symbol | Morse Code symbol |
---|---|
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 | ----. |
Converting String to Morse Code requirements
Below we have listed the requirements for converting a C# string to Morse code.
- Only characters from A-Z, a-z, or 0-9 are valid as input
- Space between words shall consist of a single space character
- Extra whitespace before or after words is not valid
- Empty input is not valid
- Null input is not valid
Converting Morse Code to String requirements
Below we have listed the requirements for converting a Morse Code to a C# string.
- Only Morse Code letters and numbers in the Morse Code dictionary above are valid input
- Space between Morse Code letters shall be a single space
- Space between Morse code words shall be single a forward slash character surrounded by a single space on each side ( / )
- Empty input is not valid
- Null input is not valid
Testing
Now that we have the requirements for valid input for converting a C# String to and from Morse Code, we can define our test cases below.
Valid C# String to Morse Code test cases
Below are the test cases which will be used to determine whether a C# String input is valid Morse Code.
Test case | Test input | Expected output |
---|---|---|
Single letter | A | .- |
Multiple letters with spaces | A B C D | .- / -... / -.-. / -.. |
Common phrase | HELLO WORLD | .... . .-.. .-.. --- / .-- --- .-. .-.. -.. |
Extended sentence | THERE IS NO SPOON | - .... . .-. . / .. ... / -. --- / ... .--. --- --- -. |
Invalid C# String to Morse Code test cases
Below are the test cases which will be used to determine whether a C# String input is invalid Morse Code.
Test case | Test input | Expected output |
---|---|---|
Null input | null | Input cannot be null. |
Empty string | "" | Input cannot be empty. |
Invalid number character | .1 | Invalid character(s) found in Morse code sequence. |
Invalid alphabetic character | ... a | Invalid character(s) found in Morse code sequence. |
Invalid special character | ..;. | Invalid character(s) found in Morse code sequence. |
Whitespace on both sides | .... | Input cannot start or end with whitespace. |
Whitespace in prefix | .... | Input cannot start or end with whitespace. |
Whitespace in suffix | .... | Input cannot start or end with whitespace. |
Whitespace in between letters | . . | Invalid character(s) found in Morse code sequence. |
Extra whitespace in between words | .... / .... | Extra whitespace between Morse code words is not allowed. |
Whitespace only | | Input cannot consist only of whitespace. |
Valid Morse Code to C# String test cases
Below are the test cases which will be used to determine whether a Morse Code input is a valid.
Test case | Morse code input | Expected output |
---|---|---|
Single letter A | .- | A |
Multiple letters with slashes | .- / -... / -.-. / -.. | A B C D |
Common phrase | .... . .-.. .-.. --- / .-- --- .-. .-.. -.. | HELLO WORLD |
Full sentence | - .... . .-. . / .. ... / -. --- / ... .--. --- --- -. | THERE IS NO SPOON |
Invalid Morse Code to C# String test cases
Below are the test cases which will be used to determine whether a Morse Code input is a invalid.
Test case | Morse code input | Expected error |
---|---|---|
Null input | null | Input cannot be null. |
Empty string | "" | Input cannot be empty. |
Whitespace only | | Input cannot consist only of whitespace. |
Whitespace in prefix | .... | Input cannot start or end with whitespace. |
Whitespace in suffix | .... | Input cannot start or end with whitespace. |
Whitespace on both sides | .... | Input cannot start or end with whitespace. |
Extra whitespace in between words | .... / .... | Invalid spacing between Morse code words or letters. |
Extra whitespace in between letters | . . | Invalid spacing between Morse code words or letters. |
Invalid alphabetic character | ... a | Invalid character(s) found in Morse code sequence. |
Invalid number character | .1 | Invalid character(s) found in Morse code sequence. |
Invalid special character | ..;. | Invalid character(s) found in Morse code sequence. |
Converting the test cases to C#
String to Morse Code C# test cases
public sealed class StringToMorseCodeConverterTests
{
[Theory]
[MemberData(nameof(StringToMorseCodeTestCases.ValidMessages), MemberType = typeof(StringToMorseCodeTestCases))]
public void For_StringToMorseCodeConverter_When_TryConvert_With_Valid_MorseCode_Then_Return_Expected_Message(string message, string expectedMessage)
{
StringToMorseCodeConverterResult.Success expectedResult = new(expectedMessage);
StringToMorseCodeConverterResult actualResult = StringToMorseCodeConverter.TryConvert(message);
Assert.Equal(expectedResult, actualResult);
}
[Theory]
[MemberData(nameof(StringToMorseCodeTestCases.InvalidMessages), MemberType = typeof(StringToMorseCodeTestCases))]
public void For_StringToMorseCodeConverter_When_TryConvert_With_Invalid_MorseCode_Then_Return_Error_Message(string? message, string expectedErrorMessage)
{
StringToMorseCodeConverterResult.Failure expectedResult = new(expectedErrorMessage);
StringToMorseCodeConverterResult actualResult = StringToMorseCodeConverter.TryConvert(message);
Assert.Equal(expectedResult, actualResult);
}
private static class StringToMorseCodeTestCases
{
public static readonly TheoryData<string, string> ValidMessages = new()
{
{ "A", ".-" },
{ "A B C D", ".- / -... / -.-. / -.." },
{ "HELLO WORLD", ".... . .-.. .-.. --- / .-- --- .-. .-.. -.." },
{ "THERE IS NO SPOON", "- .... . .-. . / .. ... / -. --- / ... .--. --- --- -." }
};
public static readonly TheoryData<string?, string> InvalidMessages = new()
{
{ null, IsNull },
{ InvalidMessage.ExtraWhitespaceInBetweenWords, ExtraWhitespaceBetweenWords },
{ InvalidMessage.Empty, IsEmpty },
{ InvalidMessage.InvalidCharacters, InvalidCharacters },
{ InvalidMessage.WhitespaceOnly, IsWhitespace },
{ InvalidMessage.WhitespacePrefix, StartOrEndingSpaces },
{ InvalidMessage.WhitespaceSuffix, StartOrEndingSpaces },
{ InvalidMessage.WhitespaceBothSides, StartOrEndingSpaces }
};
}
}
Morse Code to String C# test cases
public sealed class MorseCodeToStringParserTests
{
[Theory]
[MemberData(nameof(MorseCodeToStringParserTestCases.ValidMorseCodeData), MemberType = typeof(MorseCodeToStringParserTestCases))]
public void For_MorseCodeToStringParser_When_TryParse_With_Valid_MorseCode_Then_Return_Expected_Message(string morseCode, string expectedMessage)
{
MorseCodeToStringParseResult.Success expectedResult = new(expectedMessage);
MorseCodeToStringParseResult actualResult = MorseCodeToStringParser.TryParse(morseCode);
Assert.Equal(expectedResult, actualResult);
}
[Theory]
[MemberData(nameof(MorseCodeToStringParserTestCases.InvalidMorseCodeData), MemberType = typeof(MorseCodeToStringParserTestCases))]
public void For_MorseCodeToStringParser_When_TryParse_With_Invalid_MorseCode_Then_Return_Error_Message(string? morseCode, string expectedErrorMessage)
{
MorseCodeToStringParseResult.Failure expectedResult = new(expectedErrorMessage);
MorseCodeToStringParseResult actualResult = MorseCodeToStringParser.TryParse(morseCode);
Assert.Equal(expectedResult, actualResult);
}
private static class MorseCodeToStringParserTestCases
{
public static readonly TheoryData<string, string> ValidMorseCodeData = new()
{
{ ".-", "A" },
{ ".- / -... / -.-. / -..", "A B C D" },
{ ".... . .-.. .-.. --- / .-- --- .-. .-.. -..", "HELLO WORLD" },
{ "- .... . .-. . / .. ... / -. --- / ... .--. --- --- -.", "THERE IS NO SPOON" }
};
public static readonly TheoryData<string?, string> InvalidMorseCodeData = new()
{
{ null, IsNull },
{ InvalidMorseCode.Empty, IsEmpty },
{ InvalidMorseCode.WhitespaceOnly, IsWhitespace },
{ InvalidMorseCode.WhitespacePrefix, StartOrEndingSpaces },
{ InvalidMorseCode.WhitespaceSuffix, StartOrEndingSpaces },
{ InvalidMorseCode.WhitespaceBothSides, StartOrEndingSpaces },
{ InvalidMorseCode.WhitespaceInBetweenWords, InvalidSpacingBetweenWordsOrLetters },
{ InvalidMorseCode.WhitespaceInBetweenLetters, InvalidSpacingBetweenWordsOrLetters },
{ InvalidMorseCode.InvalidAlphaCharacter, InvalidMorseCodeCharacter },
{ InvalidMorseCode.InvalidNumberCharacter, InvalidMorseCodeCharacter },
{ InvalidMorseCode.InvalidSpecialCharacter, InvalidMorseCodeCharacter }
};
}
}
Implementing the C# String to Morse Code converter
Below we will break down the implementation of the C# String to Morse Code converter. You can see the full source code of the implementation later on as well.
Defining the map between C# characters and Morse Code strings
Firstly we define the map between an alphabetical character or number, and the representative Morse Code string. We can do this via C# Dictionary collection.
private static readonly Dictionary<char, string> _alphabet = new()
{
['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'] = "----."
};
The key is of the type char, and the value is of the type string.
Define character and word constants
Next we will define some constants we can use later on in our converter function.
private const char _morseCodeLetterSeparator = ' ';
private const string _morseCodeWordSeparator = " / ";
private const string _wordSeparator = " ";
- _morseCodeLetterSeparator is used to denote the character separating Morse Code letters
- _morseCodeWordSeparator is used to denote the characters separating Morse Code words
- _wordSeparator is used to denote the character separating the words in the input
Implementing the Morse Code to C# String parser
Below we will break down the implementation of the Morse Code to C# String parser. You can see the full source code of the implementation later on as well.
Summary
Full implementation code
Below you can find the full implementations of both the C# String to Morse Code converter and Morse Code to C# parser.
C# String to Morse Code Converter
internal static class StringToMorseCodeConverter
{
private static readonly Dictionary<char, string> _alphabet = new()
{
['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'] = "----."
};
private const char _morseCodeLetterSeparator = ' ';
private const string _morseCodeWordSeparator = " / ";
private const string _wordSeparator = " ";
public static StringToMorseCodeConverterResult TryConvert(string? message)
{
if (message is null)
{
return new StringToMorseCodeConverterResult.Failure(IsNull);
}
if (message == string.Empty)
{
return new StringToMorseCodeConverterResult.Failure(IsEmpty);
}
if (string.IsNullOrWhiteSpace(message))
{
return new StringToMorseCodeConverterResult.Failure(IsWhitespace);
}
if (message.Trim().Length != message.Length)
{
return new StringToMorseCodeConverterResult.Failure(StartOrEndingSpaces);
}
string[] words = message.Split(_wordSeparator);
if (words.Any(string.IsNullOrWhiteSpace))
{
return new StringToMorseCodeConverterResult.Failure(ExtraWhitespaceBetweenWords);
}
StringBuilder result = new();
foreach (string word in words)
{
IList<string> morseCodeWord = new List<string>();
foreach (char letter in word)
{
char normalisedLetter = char.ToUpperInvariant(letter);
if (!_alphabet.TryGetValue(normalisedLetter, out var morseCodeCharacter))
{
return new StringToMorseCodeConverterResult.Failure(InvalidCharacters);
}
morseCodeWord.Add(morseCodeCharacter);
}
result.Append(string.Join(_morseCodeLetterSeparator, morseCodeWord));
if (word != words.Last())
{
result.Append(_morseCodeWordSeparator);
}
}
return new StringToMorseCodeConverterResult.Success(result.ToString().TrimEnd());
}
}
Morse Code to C# String parser
internal static class MorseCodeToStringParser
{
private static readonly Dictionary<string, char> _alphabet = new()
{
[".-"] = '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'
};
private const char _letterSeparator = ' ';
private const string _wordSeparator = " / ";
public static MorseCodeToStringParseResult TryParse(string? morseCode)
{
if (morseCode is null)
{
return new MorseCodeToStringParseResult.Failure(IsNull);
}
if (morseCode == string.Empty)
{
return new MorseCodeToStringParseResult.Failure(IsEmpty);
}
if (string.IsNullOrWhiteSpace(morseCode))
{
return new MorseCodeToStringParseResult.Failure(IsWhitespace);
}
if (morseCode.Trim().Length != morseCode.Length)
{
return new MorseCodeToStringParseResult.Failure(StartOrEndingSpaces);
}
string[] words = morseCode.Split(_wordSeparator);
StringBuilder result = new();
foreach (string word in words)
{
string[] letters = word.Split(_letterSeparator);
if (letters.Any(string.IsNullOrWhiteSpace))
{
return new MorseCodeToStringParseResult.Failure(InvalidSpacingBetweenWordsOrLetters);
}
foreach (string letter in letters)
{
if (_alphabet.TryGetValue(letter, out char character))
{
result.Append(character);
}
else
{
return new MorseCodeToStringParseResult.Failure(InvalidMorseCodeCharacter);
}
}
result.Append(_letterSeparator);
}
return new MorseCodeToStringParseResult.Success(result.ToString().TrimEnd());
}
}
View the source code for this article on GitHub