简体   繁体   中英

Implementing the Caesar cipher algorithm in Python

So I'm trying to do a code that will shift every letter in a word back by a number of letters in the alphabet (wrapping around for the end). For example, if I want to shift by 2 and I input CBE, I should get AZC. or JOHN into HMFL. I got a code to work for only one letter, and I wonder if there's a way to do a nested for loop for python (that works?)

def move(word, shift):
  alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZ"
  original = ""
  for letter in range(26, len(alphabet)):
    if alphabet[letter] == word: #this only works if len(word) is 0, I want to be able to iterate over the letters in word.
        original += alphabet[letter-shift]
  return original

You could start like this

def move(word, shift):
    alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
    return "".join([alphabet[alphabet.find(i)-shift] for i in word])

Basically, this list comprehension constructs a list of the single letters. Then, the index of the letter in the alphabet is found by the .find method. The (index - shift) is the desired new index, which is extracted from alphabet. The resulting list is joined again and returned.

Note that it does obviously not work on lowercase input strings (if you want that use the str.upper method). Actually, the word should only consist of letters present in alphabet. For sentences the approach needs to treat whitespaces differently.

Don't find the letter in the alphabet that way -- find it with an index operation. Let char be the letter in question:

alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
...
char_pos = alphabet.index(char)
new_pos = (char_pos - shift) % len(alphabet)
new_char = alphabet[new_pos]

Once you understand this, you can collapse those three lines to a single line.

Now, to make it operate on an entire word ...

new_word = ""
for char in word:
    # insert the above logic
    new_word += new_char

Can you put all those pieces together?

You'll still need your check to see that char is a letter. Also, if you're interested, you can build a list comprehension for all the translated characters and the apply ''.join() to get your new word.

For instance ...

If the letter is in the alphabet (if char in alphabet), shift the given distance and get the new letter, wrapping around the end if needed (% 26). If it's not a capital letter, then use the original character.

Make a list from all these translations, and then join them into a string. Return that string.

def move(word, shift):
    alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
    return ''.join([alphabet[(alphabet.find(char) - shift) % 26]
               if char in alphabet else char 
               for char in word])

print move("IBM", 1)
print move("The 1812 OVERTURE is COOL!", 13)

Output:

HAL
Ghe 1812 BIREGHER is PBBY!
A_VAL = ord('a')

def move(word, shift):
    new_word = ""
    for letter in word:
        new_letter = ord(letter) - shift
        new_word += chr(new_letter) if (new_letter >= A_VAL) else (26 + new_letter)
    return new_word

Note that this will only work for lowercase words. As soon as you start mixing upper and lowercase letters you'll need to start checking for them. But this is a start. I discarded your nested loop idea because you should avoid those if at all possible.

You could use : chr() give you the character for a ascii number, ord() give you the ascii number for the matching character.

Here is an old Vigenere project :

def code_vigenere(ch,cle):
    text = ch.lower()
    clef = cle.lower()
    L = len(cle)
    res = ''

    for i,l in enumerate(text):
        res += chr((ord(l) - 97 + ord(cle[i%L]) - 97)%26 +97)

    return res

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM