Python Forum
Learning Python with a Caesar cipher
Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
Learning Python with a Caesar cipher
#1
Hi!

I’m learning about Caesar ciphers. I attempted to write my own from scratch some time ago which I only partially got working. I’m starting fresh, this time learning from sample code from Tutorials Point.

Here is their script with slight modifications that I have made:

message = 'Hello' #encrypted message
LETTERS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
 
def encrypt(message, LETTERS):
   for key in range(len(LETTERS)):
       translated = ''
       for character in message:
           if character in LETTERS:
               num = LETTERS.find(character)
               num = num - key
               if num < 0:
                   num = num + len(LETTERS)
               translated = translated + LETTERS[num]
           else:
               translated = translated + character
   return translated
 
print(f"Encrypted message : {encrypt(message, LETTERS)}")
Here is my expected output:

Quote:$ python script.py
Encrypted message : Ifmmp

Where each letter is shifted 1 character to the right.

However here is my actual output:

Quote:$ python script.py
Encrypted message : Iello

It appears only the first letter is shifted.

These are my questions for all of you:
  1. Why is only the first letter shifted when all the letters should be shifted?
  2. How might you people modify the algorithm to shift one character to the right?
Reply
#2
That's a lot of work your program is doing....

1) LETTERS has only uppercase contents. Hello only has one uppercase letter, so that's the only one modified. If you instead feed in "HELLO", all the letters will be translated.

2) You're actually translating the string 26 times (that's the outer loop) and only printing out the last one. Rather than looping with key set to a lot of different things, you could just set it to 25 and then run the inner loop once (since you throw away all the other translations).
Reply
#3
FYI: Whenever you need a string of uppercase letters you can use string.ascii_uppercase
string.ascii_lowercase for lower case

Example:
>>> import string
>>> LETTERS = string.ascii_uppercase
>>> LETTERS
'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
>>> LETTERS = string.ascii_lowercase
>>> LETTERS
'abcdefghijklmnopqrstuvwxyz'
Reply
#4
(Nov-21-2020, 09:55 AM)Larz60+ Wrote: FYI: Whenever you need a string of uppercase letters you can use string.ascii_uppercase
string.ascii_lowercase for lower case

Example:
>>> import string
>>> LETTERS = string.ascii_uppercase
>>> LETTERS
'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
>>> LETTERS = string.ascii_lowercase
>>> LETTERS
'abcdefghijklmnopqrstuvwxyz'

(Nov-21-2020, 07:31 AM)bowlofred Wrote: That's a lot of work your program is doing....

1) LETTERS has only uppercase contents. Hello only has one uppercase letter, so that's the only one modified. If you instead feed in "HELLO", all the letters will be translated.

Thank you both for your clarification. Where I declared LETTERS originally, I used all upper case letters typed by hand. I changed that line so my LETTERS now reads: LETTERS = string.ascii_letters which provides the full english alphabet (upper case and lower case).


With those changes, here is my script in full:

# Second sample code from Tutorials Point: https://www.tutorialspoint.com/cryptography_with_python/cryptography_with_python_caesar_cipher.htm
import string

message = 'Hello World' #encrypted message
LETTERS = string.ascii_letters


def encrypt(message, LETTERS):
    for key in range(len(LETTERS)):
        translated = ''
        for character in message:
            if character in LETTERS:
                num = LETTERS.find(character)
                num = num - key
                if num < 0:
                    num = num + len(LETTERS)
                translated = translated + LETTERS[num]
            else:
                translated = translated + character
    return translated

print(f"Encrypted message : {encrypt(message, LETTERS)}")
Quote:2) You're actually translating the string 26 times (that's the outer loop) and only printing out the last one. Rather than looping with key set to a lot of different things, you could just set it to 25 and then run the inner loop once (since you throw away all the other translations).

I’m not sure I completely understand your suggestion but I also don’t fully understand how or why my script runs the way it does.

When I run my script, the output I am expecting and actual output matches:

$ python script.py
Encrypted message : Ifmmp Xpsme
Using the VS Code debugger, I see that the two for loops generate a cipher many, many times over and over, what seems like almost perpetually. It seems to go on forever.

Why does the final translated output characters shifted to the right by one variance when I don’t specify a variance?

If I wanted to rotate 3 characters instead of 1, where in my script should I specify that? Usually the variance is specified by the person doing the encrypting, typically as a parameter / argument in the function.

Is there a better, more efficient approach to a Caesar cipher in Python that you people might recommend so that the script performs 1 translation instead of going all the way through 25?
Reply
#5
I think the problem most people have with the cipher is they think about ascii letters and not about symbols. You are looking up symbols in an input alphabet and replacing them with the associated symbol from an output alphabet 'A' is not 65, it is 'A' and ordinal value of 'A' should depend on the encryption alphabet and not the ascii alphabet.

Maybe it is clearer with an example. This is the way I am used to seeing the cipher written.
import string
  
def encrypt(message, shift=0, replace='', alphabet=string.ascii_letters):
    reply = ''
    for letter in message:
        try:
            # Get ord value of letter in alphabet
            index = alphabet.index(letter)
            # Shift ord value
            index = (index+shift) % len(alphabet)
            # Convert shifted ord value back to a letter
            reply += alphabet[index]
        except:
            reply += replace # Use replace for letters not found in alphabet
    return reply

message = input('Message to encrypt: ')
encrypted = encrypt(message, shift=8, replace=' ')
decrypted = encrypt(encrypted, shift=-8, replace=' ')
print(encrypted)
print(decrypted)
Output:
Message to encrypt: The quick brown fox jumped over the lazy dog. bpm yCqks jzwEv nwF rCuxml wDmz Bpm tiHG lwo The quick brown fox jumped over the lazy dog
That works, but I think it obfuscates what is really going on. This is what's really happening.
import string
  
def encrypt(message, shift=0, replace='', alphabet=string.ascii_letters):
    # Define the input-output replacement map
    size = len(alphabet)
    code = {a:alphabet[(index+shift) % size] for index, a in enumerate(alphabet)}
    # Encrypt the message using the replacement map
    return ''.join([code.get(letter, replace) for letter in message])

message = input('Message to encrypt: ')
encrypted = encrypt(message, shift=8, replace=' ')
decrypted = encrypt(encrypted, shift=-8, replace=' ')
print(encrypted)
print(decrypted)
Output:
Message to encrypt: Now is the time for all good men to come to the aid of their country. VwE qA Bpm Bqum nwz itt owwl umv Bw kwum Bw Bpm iql wn Bpmqz kwCvBzG Now is the time for all good men to come to the aid of their country
Reply
#6
(Nov-21-2020, 12:05 PM)Drone4four Wrote: Why does the final translated output characters shifted to the right by one variance when I don’t specify a variance?

The variance is controlled in your script by key. You set it to every number between 0 and 25, which is how many letter it will shift to the left.

You then encode with that key and do it again.

After you have finished all the encodings, you stop and print it out. So only the final encoding (with key = 25) is printed.

Quote:If I wanted to rotate 3 characters instead of 1, where in my script should I specify that? Usually the variance is specified by the person doing the encrypting, typically as a parameter / argument in the function.

Set key to that specific value.

Quote:Is there a better, more efficient approach to a Caesar cipher in Python that you people might recommend so that the script performs 1 translation instead of going all the way through 25?

Your inner loop is doing that. Your outer loop is just repeating and throwing away the work the inner loop does.
Reply


Possibly Related Threads…
Thread Author Replies Views Last Post
  Cesar Cipher ForsakenDusk 5 452 Apr-07-2024, 04:30 PM
Last Post: Pedroski55
Question Rsa Cipher Paragoon2 3 630 Nov-27-2023, 12:30 PM
Last Post: snippsat
  RSA Cipher with blocks Paragoon2 0 492 Nov-26-2023, 04:35 PM
Last Post: Paragoon2
  Caesar Cipher Help pbrowne 2 2,165 Jun-30-2021, 02:36 PM
Last Post: deanhystad
  Coding caesar's cipher drewbty 3 2,773 May-16-2020, 10:05 AM
Last Post: DPaul
  Can someone please help me convert this simple C ROT cipher code to Python code? boohoo9 5 3,453 Jun-14-2019, 03:02 PM
Last Post: DeaD_EyE
  Use nmap inside my python code to get supported cipher suites jimmeh 4 5,217 May-30-2019, 01:07 PM
Last Post: jimmeh
  Caesar Cypher--- I don't understand why it doesn't work ironsheep 12 5,880 Nov-03-2018, 06:53 PM
Last Post: j.crater
  Newbie: Help with code related to Caesar Cipher jessiblah 2 3,396 May-15-2018, 04:28 PM
Last Post: nilamo
  Problem with caesar cipher lucaron 2 2,945 Feb-05-2018, 05:17 PM
Last Post: lucaron

Forum Jump:

User Panel Messages

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