userPhrase = input('Enter a single word phrase to be encrypted: ')
userPhrase = userPhrase.lower()
key = int(input('Enter a key: '))
encryptedPhrase = ''
for character in userPhrase:
x = ord(character) - 97
x += key
x = x % 26
encryptedPhrase += chr(x + 97)
print('Encrypted phrase is ' + encryptedPhrase)
I've written a simple encryption program above, mainly following guides online, however all the material I've read through doesn't adequately explain why the ASCII value of the encrypted character is subtracted/reduced by the ASCII value of A(97). As in the line:
x = ord(character) - 97
Any explanations would be great, thank you in advance!
In ASCII, the lower case alphabet a-z starts at point ord('a') == 97. Therefore, ord(character) - 97 maps a-z to the integer range 0...25.
Your encryption, which is a Caesar cipher, then shifts that range to the right by the value of key, wrapping around due to x = x % 26. So for example, if key is 5, 0 maps to 5, 20 maps to 25, 21 maps to 0, etc.
Turning this modified alphabet back into ASCII characters, you need to add back the code point for ord('a'), hence the chr(x + 97).
Related
I am programming a Caeser Cipher encoder/decoder for my computer science class in Python 3 and I'm struggling to find out how to exclude spaces when each character is shifted.
message = input("type in a message")
messageInt = float(input("type in an integer"))
newStrs = []
for letter in message:
x = ord(letter)
x = int(x + messageInt)
newStrs.append(chr(x))
print("".join(newStrs))
This happends when I try to use an example sentance with spaces, I've tried looking online for answers but I wasn't able to find anything that seemed to work in Python 3 or would output what my teacher expects.
type in a message hello there
type in an integer 3
#khoor#wkhuh
Process finished with exit code 0
Use a simple list comprehension with a ternary to check for the spaces:
message = input("type in a message")
messageInt = int(input("type in an integer"))
print("".join([c if c == ' ' else chr((ord(c)-ord('a')+messageInt)%26+ord('a')) for c in message]))
You can easily generalize to a whitelist of characters using a set:
keep = set(' .,?!')
print("".join([c if c in keep else chr((ord(c)-ord('a')+messageInt)%26+ord('a')) for c in message]))
Output:
khoor wkhuh
To exclude spaces when shifting the characters in your message, you can simply add an if statement to your loop that checks if the current letter is a space. If it is, you can skip the shifting step and just append the space to the newStrs list.
I modified your code as an example (code updated):
# Get the message and shift amount from the user
message = input("type in a message: ")
shift = int(input("type in an integer: "))
# Create a list to store the shifted characters
new_str = []
# Iterate through each character in the message
for letter in message:
# Check if the character is a space
if letter == " ":
# If it is, append a space to the new string and skip the rest of the loop
new_str.append(" ")
continue
# Shift the character by the specified amount
# First, get the ASCII value of the character
x = ord(letter)
# Shift the character and wrap around the alphabet if necessary
x = (x + shift - 97) % 26 + 97
# Convert the shifted ASCII value back to a character and append it to the new string
new_str.append(chr(x))
# Join the shifted characters into a single string and print it
print("".join(new_str))
If you are wondering whats going on with
x = (x + shift - 97) % 26 + 97
This will shift all characters except spaces, so the output will preserve the spaces in the original message.
x = (x + shift - 97) % 26 + 97
This line of code is used to shift the ASCII value of a character by a specified amount and wrap around the alphabet if necessary.
First, the value of x is added to the value of shift:
x + shift
Then, 97 is subtracted from the result:
x + shift - 97
The result is then passed to the % operator, which returns the remainder of the division of the left operand by the right operand. In this case, the remainder of the division of x + shift - 97 by 26 is calculated:
(x + shift - 97) % 26
Finally, 97 is added back to the result:
(x + shift - 97) % 26 + 97
This has the effect of shifting the character by the specified amount, wrapping around the alphabet if necessary, and ensuring that the resulting ASCII value is in the range 97 to 122 (inclusive).
For example, if the value of x is 120 (the ASCII value of 'x'), the value of shift is 3, and the line of code is executed, the following steps will be taken:
120 + 3 = 123
123 - 97 = 26
26 % 26 = 0
0 + 97 = 97
The final result, 97, is the ASCII value of 'a', so the resulting character will be 'a'.
Currently learning Python, and working on a Caesar Cypher... I can't seem to figure out why my decryption is spitting out the wrong text; currently it does this...
Plain text: Hello
Distance: 65
Encrypt: *&..0
Decrypt: gckkm
I've looked through other posts, but don't seem to find any that have the same issue with decryption - any help would be appreciated. Thank you!
##ENCYPTION
plainText = input("Enter plain text line: ")
distance = int(input("Enter the distance value: "))
code = ""
for ch in plainText:
ordVal = ord(ch)
cipherVal = ordVal + distance
if cipherVal > 127 :
cipherVal = distance - (127 - ordVal | 1)
code += chr(cipherVal)
print(code)
##DECRYPTION
code = input("Enter the coded text: ")
distance = int(input("Enter the distance value: "))
plainText = ""
for ch in code:
ordVal = ord(ch)
cipherVal = ordVal - distance
if cipherVal < 0 :
cipherVal = 127 - (distance - (ordVal - 1))
plainText += chr(cipherVal)
print(plainText)```
I have debugged the code you posted.
lets take for example the input 'm' and distance 20.
the code will be 1 and in the ascii table this means 'SOH' - start of heading.
when trying to copy the output text in the Console and paste it, it didn't know that kind of input.
therefor, you should separate the encryption progress of numbers and letters, and make sure to make a difference between upper case and lower case.
here is the ascii table:
https://www.alpharithms.com/ascii-table-512119/
the reason for separating is to avoid non-input ascii codes.
if you want to include other signs like '-', '_', etc.
than you could ignore all 'cipherVals' between 0 to 31, or it will cause input problems as described above.
Do not worry about space and DEL, I checked and they do work in your code.
There is a major flaw in your algorithm.
Assuming your cipherMessage will be printed on paper, you will have a problem with every cipherVal between 0 and 32 because these are non-printable characters (AKA control codes).
If they do not appear on paper, how will your correspondent will be able to enter them in decrypter?
Caesar was using a 26 character set while you are using a 127 character set (32 of them being non-printable).
You can use your algorithm if you reduce your character set by excluding non-printable characters (first 32 characters plus DEL).
fcis = 32 # First Character In character Set
lcis = 126 # Last Character In character Set
scs = lcis - fcis + 1 # Size of Character Set
# TODO: REPLACE THE FOLLOWING 2 LINES WITH INPUT()
distance = 20
plainText = "Caesar Cypher Issue"
code = ""
for ch in plainText:
val = (ord(ch) - fcis + distance) % scs
code += chr( val + fcis)
print( "plainText:", plainText)
print( "codedText:", code)
#Decrypt
fcis = 32 # First Character In character Set
lcis = 126 # Last Character In character Set
scs = lcis - fcis + 1 # Size of Character Set
# TODO: ADD 2 INPUT STATEMENTS
plainText = ""
for ch in code:
val = (ord( ch) - fcis - distance + scs) % scs
plainText += chr( val + fcis)
print( "codedText:", code)
print( "plainText:", plainText)
Results:
plainText: Caesar Cypher Issue
codedText: Wuy(u'4W.%|y'4]((*y
codedText: Wuy(u'4W.%|y'4]((*y
plainText: Caesar Cypher Issue
You can even define your own custom character set, this way:
cset = " AaBbCcDdEeFfGgHh987654321"
scs = len( cset)
distance = 20
plainText = "Bach 6"
code = ""
for ch in plainText:
val = cset.find(ch)
if val<0:
print( "Unacceptabler character")
break
code += cset[ (val + distance) % scs]
print( "plainText:", plainText)
print( "codedText:", code)
# Decrypt
plaintext = ""
for ch in code:
plaintext += cset[ (cset.find( ch) - distance + scs) % scs]
print( "plainText:", plainText)
Results:
plainText: Bach 6
codedText: 34 e6g
plainText: Bach 6
Be sure your custom character set only use ASCII characters.
Unicode characters (and emojis) will not work with this algorithm.
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
I am trying to make a cryptography program.
When I run this program and insert for example abc with shift 2 it will return cde which is good. But I tried to insert xyz aswell with shift 3 and instead of shifting correctly abc it returns aaa. This also happens if I use shift 2 then it returns zaa.
How can I adjust my program to correctly start from the beginning when the alphabet is done with the ASCII tabel?
shift = int(input("Please insert a number you want to shift the characters with: "))
end = ""
for x in alf:
ascii = ord(x)
if ascii >= 97 and ascii <= 122:
res = ascii + shift
if res > 122:
res = 0 + 97
min = res + shift
end = end + chr(min)
print (end)
It is because your logic expression is wrong. Here is an example that will allow any positive integer as right shift, beginning again at a again. And it can be very much optimized (hint: use modulo operator), but this is with minor changes to your code and numerics written out loud.
for x in alf:
ascii = ord(x)
if ascii >= 97 and ascii <= 122:
res = ascii + shift
while res > 122:
res = res - (122 - 97) - 1
end = end + chr(res)
After much frustration, I have made my first Caesar Decoder :)
But the problem now is to make the program circular...
For example if we want to shift doge by 1, no problem, it's ephf...
But what about xyz, and the shift was 4???
So programming pros help a first time novice aka newb out :P
Thanks...
import string
def main():
inString = raw_input("Please enter the word to be "
"translated: ")
key = int(raw_input("What is the key value? "))
toConv = [ord(i) for i in inString] #now want to shift it by key
toConv = [x+key for x in toConv]
#^can use map(lambda x:x+key, toConv)
result = ''.join(chr(i) for i in toConv)
print "This is the final result due to the shift", result
Here is Python code that I wrote to be easy to understand. Also, I think the classic Caesar cipher didn't define what to do with punctuation; I think the classic secret messages were unpunctuated and only contained letters. I wrote this to only handle the classic Roman alphabet and pass any other characters unchanged.
As a bonus, you can use this code with a shift of 13 to decode ROT13-encoded jokes.
def caesar_ch(ch, shift):
"""
Caesar cipher for one character. Only shifts 'a' through 'z'
and 'A' through 'Z'; leaves other chars unchanged.
"""
n = ord(ch)
if ord('a') <= n <= ord('z'):
n = n - ord('a')
n = (n + shift) % 26
n = n + ord('a')
return chr(n)
elif ord('A') <= n <= ord('Z'):
n = n - ord('A')
n = (n + shift) % 26
n = n + ord('A')
return chr(n)
else:
return ch
def caesar(s, shift):
"""
Caesar cipher for a string. Only shifts 'a' through 'z'
and 'A' through 'Z'; leaves other chars unchanged.
"""
return ''.join(caesar_ch(ch, shift) for ch in s)
if __name__ == "__main__":
assert caesar("doge", 1) == "ephf"
assert caesar("xyz", 4) == "bcd"
assert caesar("Veni, vidi, vici.", 13) == "Irav, ivqv, ivpv."
The part at the end is a "self-test" for the code. If you run this as a stand-alone program, it will test itself, and "assert" if a test fails.
If you have any questions about this code, just ask and I'll explain.
Just add the key to all the actual character codes, then if the added value is greater than z, modulo with character code of z and add it with the character code of a.
inString, key = "xyz", 4
toConv = [(ord(i) + key) for i in inString] #now want to shift it by key
toConv = [(x % ord("z")) + ord("a") if x > ord("z") else x for x in toConv]
result = ''.join(chr(i) for i in toConv)
print result # cde
I'd recommend using string.translate().
So, we can do the following:
key = 1
table = string.maketrans(string.ascii_lowercase + string.ascii_uppercase, string.ascii_lowercase[key:] + string.ascii_lowercase[:key] + string.ascii_uppercase[key:] + string.ascii_uppercase[:key])
And then we can use it as follows:
'doge'.translate(table) # Outputs 'ephf'
'Doge'.translate(table) # Outputs 'Ephf'
'xyz'.translate(table) # Outputs 'yza'
In particular, this doesn't change characters that are not ascii lowercase or uppercase characters, like numbers or spaces.
'3 2 1 a'.translate(table) # Outputs '3 2 1 b'
in general, to make something "wrap" you use the modulo function (% in Python) with the number you want to wrap, and the range you want it to wrap in. For example, if I wanted to print the numbers 1 through 10 a bajillion times, I would do:
i = 0
while 1:
print(i%10+1)
# I want to see 1-10, and i=10 will give me 0 (10%10==0), so i%10+1!
i += 1
In this case it's a little more difficult because you're using ord, which doesn't have a nice happy "range" of values. If you had done something like string.ascii_lowercase you could do...
import string
codex = string.ascii_lowercase
inString = "abcdxyz"
key = 3
outString = [codex[(codex.index(char)+key)%len(codex)] for char in inString]
However since you're using ord, we're kind of going from ord('A') == 65 to ord('z')==122, so a range of 0 -> 57 (e.g. range(58), with a constant of 65. In other words:
codex = "ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz"
# every char for chr(65) -> chr(122)
codex = ''.join([chr(i+65) for i in range(58)]) # this is the same thing!
we can do this instead, but it WILL include the characters [\]^_`
inString, key = 'abcxyzABCXYZ', 4
toConv = [(ord(i)+key-65)%58 for i in inString]
result = ''.join(chr(i+65) for i in toConv)
print(result)
# "efgBCDEFG\\]^"
I know this is kind of an old topic, but I just happened to be working on it today. I found the answers in this thread useful, but they all seemed to use a decision to loop. I figured a way to accomplish the same goal just using the modulus(remainder) operator (%). This allows the number to stay within the range of a table and loop around. It also allows for easy decoding.
# advCeaser.py
# This program uses a ceaser cypher to encode and decode messages
import string
def main():
# Create a table to reference all upper, lower case, numbers and common punctuation.
table = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz1234567890,.!?-#'
print 'This program accepts a message and a key to encode the message.'
print 'If the encoded message is entered with the negative value of the key'
print 'The message will be decoded!'
# Create accumulator to collect coded message
code =''
# Get input from user: Message and encode key
message = raw_input('Enter the message you would like to have encoded:')
key = input('Enter the encode or decode key: ')
# Loop through each character in the message
for ch in message:
# Find the index of the char in the table add the key value
# Then use the remainder function to stay within range of the table.
index = ((table.find(ch)+key)%len(table))
# Add a new character to the code using the index
code = code + table[index]
# Print out the final code
print code
main()
The encode and decode output look like this.
encode:
This program accepts a message and a key to encode the message.
If the encoded message is entered with the negative value of the key
The message will be decoded!
Enter the message you would like to have encoded:The zephyr blows from the east to the west!
Enter the encode or decode key: 10
croj0ozr92jlvy73jp2ywj4rojok34j4yj4roj7o34G
decode:
This program accepts a message and a key to encode the message.
If the encoded message is entered with the negative value of the key
The message will be decoded!
Enter the message you would like to have encoded:croj0ozr92jlvy73jp2ywj4rojok34j4yj4roj7o34G
Enter the encode or decode key: -10
The zephyr blows from the east to the west!
Sorry if my formatting looks catywompus I literally found stackoverflow yesterday! Yes, I literally mean literally :)