Rohan Smith
Rohan Smith

Reputation: 11

Caesar Cipher, Python Decryption not outputting correctly

Using the Casesar Cipher algorithm, I'm trying to get it to decrypt whatever I input.

#Decrypt Code
alphabet="ABCDEFGHIJKLMNOPQRSTUVWXYZ ABCDEFGHIJKLMNOPQRSTUVWXYZ ABCDEFGHIJKLMNOPQRSTUVWXYZ "
DMessage=input("Enter your message to decrypt")
UDMessage=DMessage.upper()
lenDMessage=len(UDMessage)
offset=(int(input("Enter what you would like your offset to be 0,25")))
offset=offset-2
for x in range(0,lenDMessage):
    for y in range(-25,25):
        if UDMessage[x]==alphabet[y+25]:
            print(x,DMessage,[x], UDMessage[x],alphabet[y+25+offset])

The input is Caesar code test run, but it comes out as ecguctbeqfgbvgubbtwp. Does anyone know how to make it so I can go backwards on the alphabet to decrypt it?

Upvotes: 1

Views: 2107

Answers (4)

radtek
radtek

Reputation: 36280

I took your code and refactored it a little. Try using mod instead of extending the alphabet string like you did.

#Decrypt Code  DEFGHIJKLMNOPQRSTUVWXYZABC -> ABCDEFGHIJKLMNOPQRSTUVWXYZ  (left 3 offset, -3)
import string
alphabet = string.ascii_uppercase
cipher =  raw_input("Enter your message to decrypt: ").upper()
offset=(int(raw_input("What is the cipher offset? : ")))
plain = []
for letter in cipher:
    offset_letter = alphabet[(alphabet.index(letter) + offset) % len(alphabet)]
    plain.append(offset_letter)
print ''.join(plain)

Here is the working scenario, note -3 means shift to left by 3. So you can use the same function to encrypt /decrypt so long as you know how to shift it:

$Enter your message to decrypt: DEFGHIJKLMNOPQRSTUVWXYZABC
$What is the cipher offset? : -3
ABCDEFGHIJKLMNOPQRSTUVWXYZ

Now back the other way, doing a right shift:

$Enter your message to decrypt: ABCDEFGHIJKLMNOPQRSTUVWXYZ
$What is the cipher offset? : 3
DEFGHIJKLMNOPQRSTUVWXYZABC

Cheers

Upvotes: 0

gboffi
gboffi

Reputation: 25023

Disclaimer

This code works, sort of, with Python 3 and doesn't work, sort of, with Python 2

Code

In [1]: def shift(c,n=13,alphabet="ABCDEFGHIJKLMNOPQRSTUVWXYZ"):
        u = c.upper()
        if u not in alphabet: return c
        s = alphabet[(alphabet.index(u)+n)%26] 
        if u!=c: s=s.lower()
        return s
   ...: 

In [2]: def caesar(s,n=13): return "".join(shift(c,n) for c in s)

In [3]: def decaesar(s,n=13): return "".join(shift(c,-n) for c in s)

In [4]: caesar('Puffo Burlone, dove vai?')
Out[4]: 'Chssb Oheybar, qbir inv?'

In [5]: decaesar(_)
Out[5]: 'Puffo Burlone, dove vai?'

In [6]: caesar('Puffo Burlone, dove vai?',22)
Out[6]: 'Lqbbk Xqnhkja, zkra rwe?'

In [7]: decaesar(_,22)
Out[7]: 'Puffo Burlone, dove vai?'

In [8]: caesar('Puffo Burlonè, dovè vai?') # note the accented letters
Out[8]: 'Chssb Oheybaè, qbiè inv?'

In [9]: decaesar(_)
Out[9]: 'Puffo Burlonè, dovè vai?'

Discussion

The core of the stuff is the shift function, that examines if an uppercased character is in the alphabet and if not, it returns it as is, otherwise it shifts it by n characters (n can be negative) around an alphabetical donut, as you get using the modulo operator %, then if c is lowercase it returns the shifted, lowecased character, otherwise it returns the shifted character that, coming from an uppercase alphabet, is uppercased.

Then we have the caesar and decaesar functions, that joins the shifted characters taken out of the first, required argument, the optional argument is the amount of the shift, by default 13; the difference is that, in decaesar, shift is called with a negative n so that we undo the shift.

Eventually we test our functions, that works also with non-ascii characters, that are left alone in the encrypted string... the above is with Python 3 btw, with Python 2 non-ascii characters are mangled.

Upvotes: 1

Phistrom
Phistrom

Reputation: 581

You could use the find method to get the index of a letter in the alphabet and apply the offset to that. Something like:

import string
alphabet = string.ascii_uppercase + ' '  # more convenient than typing it out
cipher_text = ''
for c in ud_message:  # for each letter in the source message
    index = alphabet.find(c)  # get the alphabet position (A=0, B=1...)
    new_index = (index + offset) % len(alphabet)  # add offset
    cipher_text += alphabet[new_index]  # append it to our cipher_text output

The % len(alphabet) part is to wrap the index around. If the letter is Z and they have an offset of 4, the index will be 25 + 4 = 29 which is more than the length of the alphabet (26). The % len(alphabet) turns this into 29 % 26 = 3, which turns that Z into a D.

To decrypt it, you'll just do the .find() method again but this time subtract the offset and get the letter that corresponds to that index. You don't have to worry about negative indexes from subtracting the offset. In Python, a negative index starts from the other side. So for instance alphabet[24] is Y, and alphabet[-2] is also Y.

Upvotes: 0

Brad Budlong
Brad Budlong

Reputation: 1785

I can't tell exactly implementation that you used to encrypt the message, so I'm providing a version of your code that first encrypts the message and then takes that and decrypts it back.

The important thing to understand is converting a character to a number. That is what the ord() function does. By then subtracting the number for 'A' you can get an index into the letter array. You don't really need the letter array since you can use the inverse of ord() which is the chr() function, but I left the implementation as close to yours as possible. Since the ' ' is not in sequence with the other letters, I special cased for that character. With your array, I guess I could have used the string find() method instead to get a conversion to a number.

I don't know why you took the offset that was entered and subtracted 2 from it, so I left that out.

The code below will encrypt and then decrypt. Because of how negative indexes work in Python you can reverse the order and decrypt and then encrypt by entering a negative offset instead of a positive offset.

alphabet="ABCDEFGHIJKLMNOPQRSTUVWXYZ ABCDEFGHIJKLMNOPQRSTUVWXYZ ABCDEFGHIJKLMNOPQRSTUVWXYZ "
DMessage=input("Enter your message to decrypt")
UDMessage=DMessage.upper()
lenDMessage=len(UDMessage)
offset=(int(input("Enter what you would like your offset to be 0,25")))
encrypted = ''
for letter in UDMessage:
    if letter == ' ':
        index = 26
    else:
        index = ord(letter) - ord('A')
    encrypted += alphabet[index+offset]
print(encrypted)

decrypted = ''
for letter in encrypted:
    if letter == ' ':
        index = 26
    else:
        index = ord(letter) - ord('A')
    decrypted += alphabet[index+27-offset]
print(decrypted)

Upvotes: 0

Related Questions