Report this

What is the reason for this report?

Morse Code Decoder Fails on Consecutive Spaces and Special Characters

Posted on May 13, 2026

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:

  • Extra spaces create empty decoded characters
  • Unsupported symbols like @ break the parser
  • Sometimes I get KeyError exceptions

Current 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:

  • 1 space between letters
  • 3 spaces (or a /) between words

Normalize 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

  • Strict mode: if you’d rather reject invalid input than substitute ?, 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}")
    
  • Encoder symmetry: when building the encoder, emit ' ' between letters and ' ' (three spaces) between words so a round-trip decode(encode(x)) == x.upper() holds for supported characters.
  • Regex alternative: 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.

The developer cloud

Scale up as you grow — whether you're running one virtual machine or ten thousand.

Start building today

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