My Python Caeser Cipher program stops shifting after 30

Viewed 110

I created a function to split inputed string into list of words and then replace the letters in each word with its shifted counterpart but when I set the shift to over 30 it prints unchanged.

def ceaser_cipher_encoder(string , num):
        alphabet = ["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"]

        new_string = ""
        string_list = string.split(" ")
        new_list = []
        for word in string_list:
                word1 = ""
                for charecter in word:
                        letter_position = alphabet.index(charecter)
                        letter_position_with_shift = letter_position + num
                        if letter_position_with_shift > 25:
                                letter_position_with_shift = 0 + ((letter_position - 25) - 1)
                        word1 += charecter.replace(charecter, alphabet[letter_position_with_shift])

                new_list.append(word1)
                end_string = " ".join(new_list)



        return end_string




message = ceaser_cipher_encoder("hello dad", 35)
print(message)
3 Answers

One useful trick here is to use the modulus operator (%). It will take care of the shift for you.

Here is how I would do :

def ceaser_cipher_encoder(string , num):
        alphabet = ["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"]

        new_string = ""
        for c in string:
                new_string += alphabet[(alphabet.index(c) + num) % len(alphabet)]
        return new_string

Let's say c is "y" and num is 10. You would then have alphabet.index(c) equal to 24, so the shift would return 34. Since 34 modulo 26 is 8, it would append alphabet[8] ("i") to new_string.

I used len(alphabet) instead of hard-coding 26 so that you can change your alphabet and the code would still work.

The problem is with your if statement:

if letter_position_with_shift > 25:
    letter_position_with_shift = 0 + ((letter_position - 25) - 1)

should really be:

if letter_position_with_shift > 25:
    letter_position_with_shift = 0 + ((letter_position_with_shift - 25) - 1)

That being said, it would be better to use the modolo operator instead of an if statement here, to handle any multiple of 26, not just 26-52. Think about the case where num = 100, for example.

Try changing your for loop body to:

letter_position = alphabet.index(charecter)
letter_position_with_shift = (letter_position + num) % 26
word1 += charecter.replace(charecter, alphabet[letter_position_with_shift])

I would also suggest replacing the last line with:

word1 += alphabet[letter_position_with_shift]

Because you are appending that letter to word1 anyway, you don't really need to call replace on a character to get alphabet[letter_position_with_shift]

If you want to make your code a bit shorter, then you can try this:

def cipher(plain: str, offset: int) -> str:
    abc = 'abcdefghijklmnopqrstuvwxyz'
    return plain.translate(str.maketrans({c: abc[(idx + offset) % len(abc)] for idx, c in enumerate(abc)}))

print(cipher("hello dad", 35))  # Output: qnuux mjm

Further reading:

Related