Implementing the Ceaser Cipher function through input in Python - python

Im trying to create a Ceaser Cipher function in Python that shifts letters based off the input you put in.
plainText = input("Secret message: ")
shift = int(input("Shift: "))
def caesar(plainText, shift):
cipherText = ""
for ch in plainText:
if ch.isalpha():
stayInAlphabet = ord(ch) + shift
if stayInAlphabet > ord('z'):
stayInAlphabet -= 26
finalLetter = chr(stayInAlphabet)
cipherText += finalLetter
print(cipherText)
return cipherText
caesar(plainText, shift)
For example, if I put "THE IDES OF MARCH" as my message and put 1 as my shift, it outputs "UIFJEFTPGNBSDI" when it is meant to output "UIF JEFT PG NBSDI." It doesn't keep the spaces and also shifts things like exclamation marks back also when it should leave them as is. Letters should also wrap meaning if I put shift as 3, an X should go back to A.

To fix the spacing issue, you can add an else to if ch.isalpha() and just append the plain text character to the cipher text. This will also handle punctuation and other special, non-alpha characters.
To handle wrapping (e.g. X to A), you'll want to use the modulo operator %. Because A is the 65th ASCII character and not the 0th, you'll need to zero-base the alpha characters, then apply the mod, then add back the offset of 'A'. To shift with wrap-around, you can do something like: final_letter = chr((ord(ch) + shift - ord('A')) % 26 + ord('A')). Note the 26 comes from number of letters in the Latin alphabet.
With these in mind, here is a full example:
plain_text = input("Secret message: ")
shift = int(input("Shift: "))
def caesar(plain_text, shift):
cipher_text = ""
for ch in plain_text:
if ch.isalpha():
final_letter = chr((ord(ch) + shift - ord('A')) % 26 + ord('A'))
cipher_text += final_letter
else:
cipher_text += ch
print(cipher_text)
return cipher_text
caesar(plain_text, shift)
Sample input:
plain_text = "THE IDES OF MARCH"
shift = 1
cipher_text = caesar(plain_text, shift)
print(cipher_text)
# UIF JEFT PG NBSDI

The reason the cipher does not produce the expected result is your code does not account for the case where it is not a alpha non numerical letter. So, a potential fix is just adding handling for spaces.
Code
plainText = input("Secret message: ")
shift = int(input("Shift: "))
def caesar(plainText, shift):
cipherText = ""
for ch in plainText:
if ch.isalpha():
stayInAlphabet = ord(ch) + shift
if stayInAlphabet > ord('z'):
stayInAlphabet -= 26
finalLetter = chr(stayInAlphabet)
cipherText += finalLetter
elif ch is " ":
cipherText += " "
print(cipherText)
return cipherText
caesar(plainText, shift)
Example
Secret message: THE IDES OF MARCH
Shift: 1
UIF JEFT PG NBSDI

Related

Why is ROT 13 displaying g in spaces?

I am writing code that is based on ROT13 Algorithm but when I have the message as "ROT ALGORITHM" it displays as "EBGgNYTBEVGUZ". I am unsure whether the 'g' is wrong since its meant to be a space between ROT and ALGORITHM?
def rot13(message,shift):
result = ""
for i in range(len(message)):
char = message[i]
if (char.isupper()):
result += chr((ord(char) + shift-13) % 26 + 65)
else:
result += chr((ord(char) + shift-13) % 26 + 97)
return result
shift = 13
message = "ROT ALGORITHM"
print("Shift:", shift)
print(message)
print(rot13(message,shift))
From ROT13 spec, only letters should be affected by the algorithm, here as space is not upper() you go in the else section
You may handle the 2 min usecases : lowercase and uppercase, and just use the alphabet to rotate
from string import ascii_lowercase, ascii_uppercase
def rot13(message, shift):
result = ""
for char in message:
if char in ascii_uppercase:
result += ascii_uppercase[(ascii_uppercase.index(char) + shift) % 26]
elif char in ascii_lowercase:
result += ascii_lowercase[(ascii_lowercase.index(char) + shift) % 26]
else:
result += char
return result

Caesar cipher encrypt. Some issues with not properly shifting the characters

Hey so i'm trying to encrypt a string, with a shift key. It works perfectly, the only issue i have is with shifting the characters. For example hello, world!0 and the shift will be 5. I want it to return as olssv, dvysk!0 Basically, only the alphabets will get encrypted. The puncutations & numbers, etc won't be shifted.
keymax = 26
def text():
print('What message(s) are you trying to encrypt?')
return input()
def shift():
key = 0;
while True:
print('Enter the key number (1-%s)' % (keymax))
key = int(input())
if (key >= 1 and key <= keymax):
return key
def encrypt(string, shift):
hidden = ''
for char in string:
if char == ' ':
hidden = hidden + char
elif char.isupper():
hidden = hidden + chr((ord(char) + shift - 65) % 26 + 65)
else:
hidden = hidden + chr((ord(char) + shift - 97) % 26 + 97)
return hidden
text = text()
s = shift()
print("original string: ", text)
print("after encryption: ", encrypt(text, s))
I am fairly new to python, sorry for my bad understandings. Any help would gladly be appreciated!
You could replace the first if statement in your encrypt function with if char.isalpha() == False:. So when the character is not an alphabetical character, the charcter doesn't get changed.
Edit: Also to suggest an improvement, if you want to you can even have shifts upwards of 26. If you use key = key % 26 (%, called modulo, is the remainder of the division, in this case key divided by 26).
This allows you to have keys that are more than 26. This doesn't really change much at all, I just personally like it more

Why is there a %26 (Python)?

I am trying to decipher a text from user without knowing the key in caesar cipher.
I probably understand most of it but I don't get it why they use the mod 26 on line 8
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']
print('write the message you want to decode?')
inputString = input()
inputString = inputString.lower()
transAlphabet = {}
def createDict(shift):
for i in range(0,26):
letter = alphabet[i]
transAlphabet[letter]=alphabet[(i+shift)%26]
def encodeMessage(message):
cypherText = ''
for letter in message:
if letter in transAlphabet:
cypherText = cypherText + transAlphabet[letter]
else:
cypherText = cypherText + letter
print(cypherText)
for i in range(0,26):
createDict(i)
encodeMessage(inputString)
Please would be really helpful if someone helps me out thank you!
The modulo %26 is to make the alphabet circular, when you're at z come back at a
Example for alph[(i + shift) % 26] with i=20 and shift=10
without modulo you'll like to reach index 30 in the array and it doesn't not exist
with modulo you'll reach index 30%26 = 4 in the array, letter e
But also your algo is a bt strange, I would have done the shifting on the fly for each car, but your solution may be more efficient for long text as you don't need to compute the shift everytime.
Not computing the shifted alphabet before would look like this
from string import ascii_lowercase
alphabet = ascii_lowercase
def encode_message(message, shift):
cypher_text = ''
for letter in message:
letter_idx = alphabet.index(letter)
cypher_text = cypher_text + alphabet[(letter_idx + shift) % 26]
return cypher_text
def decode_message(message, shift):
cypher_text = ''
for letter in message:
letter_idx = alphabet.index(letter)
cypher_text = cypher_text + alphabet[(26 + letter_idx - shift) % 26]
return cypher_text

Python Vigenere Cipher with required functions

I am working on an online course that has us creating a caesar cipher and vigenere cipher, but we first created two functions; one to find the position of a letter in an alphabet variable, and one to rotate one given character a given amount of times (I have seen that ord() and chr() work better, but the assignment wants us to focus on simpler concepts for now, I guess).
I was able to get the caesar function working, but am unsure as how to go forward with the vigenere cipher. I have watched many videos and looked around this site, but have not found any that allow for the preservation of spaces and non alphabetical characters. Can anyone point me in the right direction of how to start the vigenere function?
#Create function alphabet_position(letter) to turn letter into number
#such as a=0 or e=4, using lowercase to make sure case doesnt matter.
def alphabet_position(letter):
alphabet ="abcdefghijklmnopqrstuvwxyz" #Lists alphabet for a key
lower_letter = letter.lower() #Makes any input lowercase.
return alphabet.index(lower_letter) #Returns the position of input as a number.
def rotate_character(char, rot):
alphabet = "abcdefghijklmnopqrstuvwxyz"
if char.isalpha():
a = alphabet_position(char)
a = (a + rot) % 26 #needs modulo
a = (alphabet[a])
if char.isupper():
a = a.title()
return a
else:
return char
def encrypt(text, rot):
list1 = ""
for char in text:
list1 += rotate_character(char, rot)
return list1
def main():
x = input("Type a message: ")
y = input("Rotate by: ")
#result = rotate_character(x, y) #Not needed once encrypt function works.
result = encrypt(x, y)
print (result)
if __name__ == '__main__':
main()
following my comments; using all printables as alphabet:
from string import ascii_letters, digits, punctuation, whitespace
ALPHABET = ascii_letters + digits
STATIC_ALPHABET = punctuation + whitespace
# minor speedup
ALPHA_INDEX = {a: i for i, a in enumerate(ALPHABET)}
STATIC_ALPHABET_SET = set(STATIC_ALPHABET)
MOD = len(ALPHABET)
def encrypt(char, key):
if char in STATIC_ALPHABET_SET:
return char
else:
return ALPHABET[(ALPHA_INDEX[char] + key) % MOD]
def decrypt(char, key):
if char in STATIC_ALPHABET_SET:
return char
else:
return ALPHABET[(ALPHA_INDEX[char] + MOD - key) % MOD]
key = 17
plain = 'Hello World!'
enc = ''.join(encrypt(char, key) for char in plain)
print(enc) # YvCCF dFICu!
dec = ''.join(decrypt(char, key) for char in enc)
print(dec) # Hello World!

How to decode a coded sentence?

I'm not quite sure why StackOverflow won't allow me to post my code below, so I attached some links to show what I have tried. After typing out my sentence, it will shift any amount of characters from 1-25 which works just fine. However, I need to add a function which will reverse the encoding, and print the original sentence back out. I am not quite sure why Python is spitting out the decoded sentence, the way it does in the 2nd link. It should have the original sentence. Thanks for your help!
def encode( ch, shift):
lower = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWZ'
if ch not in lower:
return ch
newch = chr( ord(ch) + shift )
if newch not in lower:
newshift = ( ord(newch) - ord('z') - 1)
nwech = chr ( ord('a') + newshift )
return newch
def decoded(ch, shift):
lower = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWZ'
if ch not in lower:
return ch
newch = chr( ord(ch) + shift )
if newch not in lower:
newshift = ( ord(newch) - ord('z') - 1)
newch = chr ( ord('a') + newshift)
return newch
def main():
shift = int(input("Enter a number between 1 and 25:"))
sentence = input("Please enter a sentence:")
code = ''
decode = ''
for char in sentence:
code = code + encode (char, shift)
for char in code:
decode = decode + encode (char, shift)
print("Uncoded sentence: " + sentence)
print("Encoded sentence: " + code)
print("Decoded sentence: " + decode)
main()
Enter a number between 1 and 25:3
Please enter a sentence:i need help
Uncoded sentence: i need help
Encoded sentence: l qhhg khos
Decoded sentence: o tkkj nkrv
Your decoded function seems to be the same as your encode function (and you're not calling it in any case). Closer to what you need is:
newch = chr( ord(ch) - shift )
in your decoded function, which you should call in place of encode here:
for char in code:
decode = decode + decoded(char, shift)
But now you have another problem. What happens if your shift sends you off the end of the ord range for your characters?
You don't call the decode method. You call the encode method twice...

Categories