Python Forum
Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
Cesar Cipher
#1
Hello everyone,

I am currently doing procedural programming exercices, and there are a lot of extra outputs I want to understand before switching to OOP exercises, I still already checked some OOP concepts to see if It could help me understanding such outputs but currently I'm in a dead end.

The goal of the current exercise is to take each letter of the sentence to code and to forward it 4 rows further into the alphabet, not saving spaces.
Example : "Ave caesar" -> "ezigeiwev"
I succeed with concatenating str, but when I try adding space, I fail.
I fail too with using str.replace()

Here's my code :

ALPHABET = "abcdefghijklmnopqrstuvwxyz"
GAP = 4
chain_to_code = "ave caesar"

concatenated_chain = ""

for character in range(len(chain_to_code)):
    for letter in range(len(ALPHABET)):
        if chain_to_code[character] == ALPHABET[letter]:
            concatenated_chain += ALPHABET[(letter + GAP) % len(ALPHABET)]
            
copied_and_coded_chain = chain_to_code
                        
for character in range(len(copied_and_coded_chain)):
    for letter in range(len(ALPHABET)):
        if copied_and_coded_chain[character] == ALPHABET[letter]:
            copied_and_coded_chain = copied_and_coded_chain.replace(copied_and_coded_chain[character], ALPHABET[(letter + GAP) % len(ALPHABET)], 1)
        break
    
concatenated_chain_with_spaces = ""

for character in range(len(chain_to_code)):
    for letter in range(len(ALPHABET)):
        if chain_to_code[character] == ALPHABET[letter]:
            concatenated_chain_with_spaces += ALPHABET[(letter + GAP) % len(ALPHABET)]
        elif chain_to_code[character] == " ":
            concatenated_chain_with_spaces += " "

print(f"chain to code : {chain_to_code}")
print(f"method 1, concatenaded chain : {concatenated_chain}")
print(f"method 2, with str.replace and spaces : {copied_and_coded_chain}")
print(f"method 3, concatenaded chain with spaces : {concatenated_chain_with_spaces}")
Here's the output :
Output:
chain to code : ave caesar method 1, concatenaded chain : ezigeiwev method 2, copied and coded chain : eve ceeser method 3, concatenaded chain with spaces : ezi geiwev
I tried understanding results of method 2 & 3, but I failed.
Any help would be appreciated.

I use Win10 x64 - Python 3.10 - IDE Wing Personal 10.0.3.0

edit : for method 3 there are 26 spaces (which is len(ALPHABET)) between both words but it's showing only 1 I don't know why.
Reply
#2
Some tips and as example print both with and without spaces.
Also range(len(chain_to_code)) is not good here,just remember that range(len(sequence)) is almost always a bad way.
So just iterate over the string,no adding on is needed.
ALPHABET = "abcdefghijklmnopqrstuvwxyz"
GAP = 4
chain_to_code = "ave caesar"
encoded_without_spaces = ""
encoded_with_spaces = ""

for character in chain_to_code:
    #print(repr(character)) # Tips to see all
    if character in ALPHABET:
        encoded_char = ALPHABET[(ALPHABET.index(character) + GAP) % len(ALPHABET)]
        encoded_without_spaces += encoded_char
        encoded_with_spaces += encoded_char
    # Handle spaces
    elif character == " ":
        encoded_with_spaces += " "

print(f"Encoded without spaces: {encoded_without_spaces}")
print(f"Encoded with spaces: {encoded_with_spaces}")
Output:
Encoded without spaces: ezigeiwev Encoded with spaces: ezi geiwev
But just need this as it will handle inputs string with or without space correct.
ALPHABET = "abcdefghijklmnopqrstuvwxyz"
GAP = 4
chain_to_code = "ave caesar"
concatenated_chain = ""

concatenated_chain_with_spaces = ""
for character in chain_to_code:
    if character in ALPHABET:
        concatenated_chain += ALPHABET[(ALPHABET.index(character) + GAP) % len(ALPHABET)]
    else:
        concatenated_chain += character

print(f"Encoded: {concatenated_chain}")
Output:
Encoded: ezi geiwev
Reply
#3
Thanks for your string iteration hint.

I looked at your code and then I improved mine.
I tried again working with str.replace() but I think it's bad for this exercise :

ALPHABET = "abcdefghijklmnopqrstuvwxyz"
GAP = 4
chain_to_code = "ave caesar"

concatenated_chain = ""
concatenated_chain_with_spaces = ""

for character in chain_to_code:
    if character in ALPHABET:
        encoded_character = ALPHABET[(ALPHABET.index(character) + GAP) % len(ALPHABET)]
        concatenated_chain += encoded_character
        concatenated_chain_with_spaces += encoded_character
    elif character == " ":
        concatenated_chain_with_spaces += " "
        
chain_with_str_replace = chain_to_code
character_count = 0
            
for character in chain_with_str_replace:
    if character in ALPHABET:
        encoded_character = ALPHABET[(ALPHABET.index(character) + GAP) % len(ALPHABET)]
        chain_with_str_replace = chain_with_str_replace.replace(chain_with_str_replace[character_count], encoded_character, 1)
    character_count += 1

print(f"chain to code : {chain_to_code}")
print(f"method 1, concatenaded chain : {concatenated_chain}")
print(f"method 2, concatenaded chain with spaces : {concatenated_chain_with_spaces}")
print(f"method 3, with str.replace and spaces : {chain_with_str_replace}")
Output:
chain to code : ave caesar method 1, concatenaded chain : ezigeiwev method 2, concatenaded chain with spaces : ezi geiwev method 3, with str.replace and spaces : izi geewev
So what str.replace() does for the 3 first letters :
1. replaces "a" per "e"
2. replaces "v" per "z"
3. replaces new "e" (first letter) per "i" instead of replacing third letter "e" per "i"

I tried to fix this as you can see in my code, but then I discovered with my IDE documentation that str.replace(arg1, arg2, 1) look always for first occurrence of arg1.

So I guess it's impossible using str.replace() for this exercise, unless using 2D lists with first dimension being the character and second dimension being boolean - already replaced or not, I'll try later.
Reply
#4
str.replace() is a poor choice for this problem. Even if replace() replaced all occurrences it would still be a poor choice. After replacing all 'a' with 'e' you try to replace 'e' with 'i' resulting in replacing all a's and 'e's with i's.
def cipher(string, shift=4):
    alphabet = "abcdefghijklmnopqrstuvwxyz"

    def replace(string, a, b):
        """Replace all a's in string with b."""
        return "".join(b if c == a else c for c in string)

    encoder = dict(zip(alphabet, alphabet[shift:] + alphabet[:shift]))
    for letter in string:
        string = replace(string, letter, encoder.get(letter, ""))
    return string

print(cipher("abcdefghijklmnopqrstuvwxyz", 1))
Output:
aaaaaaaaaaaaaaaaaaaaaaaaaa
The source string must be left intact while you build a new string. I like defining the cipher using a dictionary. This one passes unrecognized characters through.
def cipher(string, shift=4):
    alphabet = "abcdefghijklmnopqrstuvwxyz"
    shift = shift % len(alphabet)
    encoder = dict(zip(alphabet, alphabet[shift:] + alphabet[:shift]))
    return "".join(encoder.get(c, c) for c in string)


x = cipher("Ave Caesar!", 2)
print(x)
print(cipher(x, -2))
Output:
Axg Ccguct! Ave Caesar!
If you want to limit the passed characters, add them to the dictionary and default dictionary.get() to return an empty string.
def cipher(string, shift=4):
    alphabet = "abcdefghijklmnopqrstuvwxyz"
    passed = " .,!?"
    shift = shift % len(alphabet)
    encoder = dict(zip(alphabet, alphabet[shift:] + alphabet[:shift]))
    encoder.update({x: x for x in passed})
    return "".join(encoder.get(c, "") for c in string)


x = cipher("Ave Caesar!", 2)
print(x)
print(cipher(x, -2))
Output:
xg cguct! ve aesar!
Reply
#5
Thanks, I discovered because of you new ways to use dictionaries. I read some IDE documentation about them.

I updated your code so it can work with capital letters :

def cipher(string, shift=4):
    alphabet = "abcdefghijklmnopqrstuvwxyz"
    alphabet_caps = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
    passed = " .,!?"
    shift = shift % len(alphabet)
    encoder = dict(zip(alphabet, alphabet[shift:] + alphabet[:shift]))
    encoder.update({x: x for x in passed})
    encoder_caps = dict(zip(alphabet_caps, alphabet_caps[shift:] + alphabet_caps[:shift]))  
    encoder.update(encoder_caps)
    return "".join(encoder.get(c, "") for c in string)
 
 
x = cipher("Ave Caesar!", 2)
print(x)
print(cipher(x, -2))
Output:
Cxg Ecguct! Ave Caesar!
Reply
#6
I learned this whilst working for MI5! (Top secret, don't tell anyone!)

from string import ascii_lowercase, ascii_uppercase, digits, punctuation, whitespace

alpha = ascii_uppercase + ascii_lowercase + digits + punctuation
len(alpha) # 94

# Thank you Franz Schubert!
text = '''Ave Maria
Gratia plena
Maria, gratia plena
Maria, gratia plena
Ave, ave dominus
Dominus tecum
Benedicta tu in mulieribus
Et benedictus
Et benedictus fructus ventris
Ventris tuae, Jesus.
Ave Maria'''

def cyberSecurity(text):
    text_list = [t for t in text]
    for i in range(len(text_list)):
        if text_list[i] in alpha:
            index = alpha.index(text_list[i])
            # go back to the beginning if index > len(alpha) - 4 
            if index > len(alpha) - 5:
                index = 4 - (len(alpha) - index )
                text_list[i] = alpha[index]
            else:
                text_list[i] = alpha[index+4]
    coded = ''.join(text_list)
    return coded

print(cyberSecurity(text))
Unbreakable code, but hard to sing!

Output:
Ezi Qevme Kvexme tpire Qevme: kvexme tpire Qevme: kvexme tpire Ezi: ezi hsqmryw Hsqmryw xigyq Firihmgxe xy mr qypmivmfyw Ix firihmgxyw Ix firihmgxyw jvygxyw zirxvmw Zirxvmw xyei: Niwyw< Ezi Qevme
Reply


Possibly Related Threads…
Thread Author Replies Views Last Post
Question Rsa Cipher Paragoon2 3 668 Nov-27-2023, 12:30 PM
Last Post: snippsat
  RSA Cipher with blocks Paragoon2 0 516 Nov-26-2023, 04:35 PM
Last Post: Paragoon2
  Caesar Cipher Help pbrowne 2 2,203 Jun-30-2021, 02:36 PM
Last Post: deanhystad

Forum Jump:

User Panel Messages

Announcements
Announcement #1 8/1/2020
Announcement #2 8/2/2020
Announcement #3 8/6/2020