13

I need to convert a string inputed by a user into morse code. The way our professor wants us to do this is to read from a morseCode.txt file, seperate the letters from the morseCode into two lists, then convert each letter to morse code (inserting a new line when there is a space).

I have the beginning. What it does is reads the morseCode.txt file and seperates the letters into a list [A, B, ... Z] and the codes into a list ['– – . . – –\n', '. – . – . –\n'...].

We haven't learned "sets" yet, so I can't use that. How would I then take the string that they inputed, go through letter by letter, and convert it to morse code? I'm a bit caught up. Here's what I have right now (not much at all...)

EDIT: completed the program!

# open morseCode.txt file to read
morseCodeFile = open('morseCode.txt', 'r') # format is <letter>:<morse code translation><\n>   
# create an empty list for letters
letterList = []    
# create an empty list for morse codes
codeList = []
# read the first line of the morseCode.txt
line = morseCodeFile.readline()    
# while the line is not empty
while line != '':        
    # strip the \n from the end of each line
    line = line.rstrip()        
    # append the first character of the line to the letterList        
    letterList.append(line[0])           
    # append the 3rd to last character of the line to the codeList
    codeList.append(line[2:])        
    # read the next line
    line = morseCodeFile.readline()        
# close the file    
morseCodeFile.close()


try:
    # get user input
    print("Enter a string to convert to morse code or press <enter> to quit")    
    userInput = input("")  
    # while the user inputs something, continue   
    while userInput:
        # strip the spaces from their input
        userInput = userInput.replace(' ', '')
        # convert to uppercase
        userInput = userInput.upper()

        # set string accumulator
        accumulateLetters = ''
        # go through each letter of the word
        for x in userInput:            
            # get the index of the letterList using x
            index = letterList.index(x)
            # get the morse code value from the codeList using the index found above
            value = codeList[index]
            # accumulate the letter found above
            accumulateLetters += value
        # print the letters    
        print(accumulateLetters)
        # input to try again or <enter> to quit
        print("Try again or press <enter> to quit")
        userInput = input("")

except ValueError:
    print("Error in input. Only alphanumeric characters, a comma, and period allowed")
    main()   
dan
  • 131
  • 1
  • 1
  • 4
  • 4
    I know your teachers would be encouraging you to comment the code, but you should learn too to use them only if they add something useful ;-) (e.g. document API's, reference where your formulae or data tables were taken from, explain why doing something in some way when there seems to be another simpler, etc.) – fortran Apr 14 '10 at 13:28
  • consider putting the try/except stuff inside the `for x in inputLetters` loop. That way you can "accept" out-of-range characters but then carry on converting all of the rest. Also, you wouldn't need to remove all of the spaces up front. – dash-tom-bang Apr 14 '10 at 13:57
  • thanks for the comments guys. dash-tom-bang, i edited the program to allow spaces by appending the list to allow it. and thanks fortran, but unfortunately the way the teacher makes us write our programs, she wants us to write it in all pseudo-code first, then go back and fill it in with the actual code. so if something isn't commented, she'll know we didn't do the pseudo-code first. i appreciate all your guys help though! – dan Apr 15 '10 at 21:10

8 Answers8

21

Why not just iterate through the string?

a_string="abcd"
for letter in a_string:
    print letter

returns

a
b
c
d

So, in pseudo-ish code, I would do this:

user_string = raw_input()
list_of_output = []
for letter in user_string:
   list_of_output.append(morse_code_ify(letter))

output_string = "".join(list_of_output)

Note: the morse_code_ify function is pseudo-code.

You definitely want to make a list of the characters you want to output rather than just concatenating on them on the end of some string. As stated above, it's O(n^2): bad. Just append them onto a list, and then use "".join(the_list).

As a side note: why are you removing the spaces? Why not just have morse_code_ify(" ") return a "\n"?

mellort
  • 940
  • 1
  • 10
  • 19
4

A couple of things for ya:

The loading would be "better" like this:

with file('morsecodes.txt', 'rt') as f:
   for line in f:
      line = line.strip()
      if len(line) > 0:
         # do your stuff to parse the file

That way you don't need to close, and you don't need to manually load each line, etc., etc.

for letter in userInput:
   if ValidateLetter(letter):  # you need to define this
      code = GetMorseCode(letter)  # from my other answer
      # do whatever you want
dash-tom-bang
  • 17,383
  • 5
  • 46
  • 62
2
# Retain a map of the Morse code
conversion = {}

# Read map from file, add it to the datastructure
morseCodeFile = file('morseCode.txt')
for line in moreCodeFile:
    conversion[line[0]] = line[2:]
morseCodeFile.close()

# Ask for input from the user
s = raw_input("Please enter string to translate")
# Go over each character, and print it the translation.
# Defensive programming: do something sane if the user 
# inputs non-Morse compatible strings.    
for c in s:
    print conversion.get(c, "No translation for "+c)
moshez
  • 36,202
  • 1
  • 22
  • 14
  • can't use this, didn't learn this much. i've edited my original post to include a bit more code. just need help finishing it off – dan Apr 13 '10 at 23:55
  • What you have "so far" does not really work, so it's hard to "finish it". What parts didn't you learn? – moshez Apr 13 '10 at 23:58
  • haven't learned maps. if you take a look at my edits, why would i not be able to use a loop to go through the string that is stripped of spaces, take the current letter and search the letterList list for its index, then use that corresponding location to find the morse code in the morseCode list, and return that – dan Apr 14 '10 at 00:01
  • Ouch. That's like writing your own maps, slowly. You can do that, but it's horrible. [That's unrelated to the fact that what you have "so far" is incorrect, really. For example, "morseCodeFile.rstrip()" will fail. But I'm not sure there's much pedagogical value in "learn to use Python without maps" -- maps the very soul of Python...] – moshez Apr 14 '10 at 00:15
  • well i fixed that rstrip() line. it's line = line.rstrip() now so that's not the prob. this sucks :( – dan Apr 14 '10 at 00:27
2

Use 'index'.

def GetMorseCode(letter):
   index = letterList.index(letter)
   code = codeList[index]
   return code

Of course, you'll want to validate your input letter (convert its case as necessary, make sure it's in the list in the first place by checking that index != -1), but that should get you down the path.

dash-tom-bang
  • 17,383
  • 5
  • 46
  • 62
  • +1 both for answering the question, but also for doing so in a way that forces the questioner to actually finish the project solo. – acrosman Apr 14 '10 at 00:26
2

I can't leave this question in this state with that final code in the question hanging over me...

dan: here's a much neater and shorter version of your code. It would be a good idea to look at how this is done and code more this way in future. I realise you probably have no further need of this code, but learning how you should do it is a good idea. Some things to note:

  • There are only two comments - and even the second is not really necessary for someone familiar with Python, they'll realise NL is being stripped. Only write comments where it adds value.

  • The with statement (recommended in another answer) removes the bother of closing the file through the context handler.

  • Use a dictionary instead of two lists.

  • A generator comprehension ((x for y in z)) is used to do the translation in one line.

  • Wrap as little code as you can in a try/except block to reduce the probability of catching an exception you didn't mean to.

  • Use the input() argument rather than print()ing first - Use '\n' to get the new line you want.

  • Don't write code across multiple lines or with intermediate variables like this just for the sake of it:

    a = a.b()
    a = a.c()
    b = a.x()
    c = b.y()
    

    Instead, write these constructs like this, chaining the calls as is perfectly valid:

    a = a.b().c()
    c = a.x().y()
    

code = {}
with open('morseCode.txt', 'r') as morse_code_file:
    # line format is <letter>:<morse code translation>
    for line in morse_code_file:
        line = line.rstrip()  # Remove NL
        code[line[0]] = line[2:]

user_input = input("Enter a string to convert to morse code or press <enter> to quit\n")
while user_input:
    try:
        print(''.join(code[x] for x in user_input.replace(' ', '').upper()))
    except KeyError:
        print("Error in input. Only alphanumeric characters, a comma, and period allowed")

    user_input = input("Try again or press <enter> to quit\n")
Chris Morgan
  • 86,207
  • 24
  • 208
  • 215
1
# Open the file
f = open('morseCode.txt', 'r')

# Read the morse code data into "letters" [(lowercased letter, morse code), ...]
letters = []
for Line in f:
    if not Line.strip(): break
    letter, code = Line.strip().split() # Assuming the format is <letter><whitespace><morse code><newline>
    letters.append((letter.lower(), code))
f.close()

# Get the input from the user
# (Don't use input() - it calls eval(raw_input())!)
i = raw_input("Enter a string to be converted to morse code or press <enter> to quit ") 

# Convert the codes to morse code
out = []
for c in i:
    found = False
    for letter, code in letters:
        if letter == c.lower():
            found = True
            out.append(code)
            break

    if not found: 
        raise Exception('invalid character: %s' % c)

# Print the output
print ' '.join(out)
cryo
  • 14,219
  • 4
  • 32
  • 35
1

For the actual processing I'd keep a string of finished product, and loop through each letter in the string they have entered. I'd call a function to convert a letter to morse code, then add it to the string of existing morse code.

finishedProduct = []
userInput = input("Enter text")
for letter in userInput:
    finishedProduct.append( letterToMorseCode(letter) )
theString = ''.join(finishedProduct)
print(theString)

You could either check for space in the loop, or in the function that is called.

Davis
  • 317
  • 2
  • 13
  • that's fine, i just have trouble splitting the string into individual letters – dan Apr 14 '10 at 00:14
  • It's a bad idea to build a string that way in Python -- it's O(N**2). Build a list, then join it with "".join(l), and print that. – moshez Apr 14 '10 at 00:17
1

Create a lookup table first:

morse = [None] * (ord('z') - ord('a') + 1)
for line in moreCodeFile:
    morse[ord(line[0].lower()) - ord('a')] = line[2:]

Then convert using the table:

for ch in userInput:
    print morse[ord(ch.lower()) - ord('a')]
Reinout van Rees
  • 13,486
  • 2
  • 36
  • 68
hamax
  • 156
  • 5
  • Ouch. Don't change the return value of range -- it happens to work, but it's not really supported or good style. If you want a list of a given length, morse = [None]*(ord('z')-(ord('a')) would work a lot better – moshez Apr 14 '10 at 00:17