By Ben Cholye
I’m building a Morse code encoder/decoder in Python and ran into an issue when decoding messages that contain multiple spaces or unsupported symbols.
The decoder works fine for simple inputs like:
… . .-… .-… —
which correctly outputs: HELLO
But when the input contains word separators or unexpected characters, the output becomes inconsistent.
Example problematic input: … . .-… .-… — .-- — .-. .-… -… @
Expected output: HELLO WORLD
Actual behavior:
@ break the parserKeyError exceptionsCurrent decoding logic:
MORSE_CODE_DICT = { ‘.-’: ‘A’, ‘-…’: ‘B’, # … }
decoded = “”
for code in morse_input.split(" "): decoded += MORSE_CODE_DICT[code]
Questions:
What’s the best way to safely handle invalid Morse sequences? How should multiple spaces be interpreted when separating words? Is there a more robust parsing strategy for Morse code decoding in Python?
Any suggestions or best practices would be appreciated.
This textbox defaults to using Markdown to format your answer.
You can type !ref in this text area to quickly search our full set of tutorials, documentation & marketplace offerings and insert the link!
These answers are provided by our Community. If you find them useful, show some love by clicking the heart. If you run into issues leave a comment, or add your own answer to help others.
Heya,
Your issues stem from three things: (1) dict[key] raises KeyError on unknown codes, (2) split(" ") produces empty strings when there are consecutive spaces, and (3) you’re not distinguishing letter separators (single space) from word separators (typically / or multiple spaces).
## The fix
### 1. Use dict.get() instead of dict[...]
dict.get(key, default) returns the default instead of raising KeyError. This alone solves the crash on @.
### 2. Standardize the word separator
The conventional Morse convention is:
/) between wordsNormalize the input first so the parser only deals with one format.
### 3. Parse in two levels: words → letters
Split on the word separator first, then on the letter separator. This makes empty tokens disappear naturally and makes the intent explicit.
## Working example
MORSE_CODE_DICT = {
'.-': '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',
}
def decode_morse(morse: str, unknown: str = '?') -> str:
# Normalize: treat "/" as a word separator, collapse runs of spaces
# into the canonical 3-space word break.
normalized = morse.replace('/', ' ').strip()
words = [w for w in normalized.split(' ') if w]
decoded_words = []
for word in words:
letters = [MORSE_CODE_DICT.get(code, unknown)
for code in word.split(' ') if code]
decoded_words.append(''.join(letters))
return ' '.join(decoded_words)
### Test
>>> decode_morse('.... . .-.. .-.. --- .-- --- .-. .-.. -..')
'HELLO WORLD'
>>> decode_morse('.... . .-.. .-.. --- .-- --- .-. .-.. -.. @')
'HELLO WORLD?'
>>> decode_morse('.... . .-.. .-.. --- / .-- --- .-. .-.. -..')
'HELLO WORLD'
## Why this works
| Problem | Cause | Fix |
|---|---|---|
KeyError on @ |
dict[key] raises on miss |
dict.get(code, '?') |
| Empty decoded chars | "a b".split(" ") yields ['a', '', 'b'] |
if code filter inside the comprehension |
| Words run together | No distinction between letter/word gaps | Split on " " first, then " " |
| Mixed separators | Some sources use /, some use |
Normalize / → " " up front |
## A couple of further notes
?, raise your own ValueError with the offending token — it’s friendlier than a bare KeyError:
if code not in MORSE_CODE_DICT:
raise ValueError(f"Invalid Morse token: {code!r}")
' ' between letters and ' ' (three spaces) between words so a round-trip decode(encode(x)) == x.upper() holds for supported characters.re.split(r' {3,}', morse.strip()) for words and re.split(r' +', word) for letters also collapses runs gracefully if you prefer not to normalize first.This pattern — normalize input → split coarse → split fine → lookup with default — generalizes well to any token-stream decoder, not just Morse.
Heya, @benner
Your main issue is that split(" ") treats every single space literally, so consecutive spaces create empty strings, which then trigger KeyError when you look them up. In Morse, single spaces usually separate letters and multiple spaces (often 3) separate words, so you need parsing logic that handles both structure and bad input gracefully.
A more reliable approach is:
Use .split(" ") for words first
Then .split() for letters inside each word
Use .get() instead of direct dict indexing so invalid symbols don’t crash decoding
Hope that this help!
Handle decoding in two steps: split words first, then decode letters. Avoid direct dictionary access because empty strings and unknown symbols will cause KeyError.
Use .get() with a fallback:
for code in morse_input.split(): decoded += MORSE_CODE_DICT.get(code, "?")
For word gaps, treat multiple spaces (commonly 3) as a word separator, not as empty letters. This keeps invalid input from breaking the parser and gives you control over how unknown Morse sequences are handled.
Get paid to write technical tutorials and select a tech-focused charity to receive a matching donation.
Full documentation for every DigitalOcean product.
The Wave has everything you need to know about building a business, from raising funding to marketing your product.
Scale up as you grow — whether you're running one virtual machine or ten thousand.

From GPU-powered inference and Kubernetes to managed databases and storage, get everything you need to build, scale, and deploy intelligent applications.
