Cryptography with ASCII in python 3 - python

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)

Related

How to keep spaces in caesar cipher program

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'.

Decrypting a Caesar Cipher with ord() and chr()?

I have an assignment for a lab that asks me to decrypt an input file that has been encrypted with a ceasar cipher. As background, my instructor has already let everyone know that the cipher has a shift distance of three.
I was able to get my to write to an output file, but my output.txt is completely unreadable, and not close at all to what the expected output should be. I remember my instructor mentioning that we could use ord() and chr() combined with arithmetic to transform the encoded characters to plaintext.
decode = inpf.readlines()
for line in decode:
output = ""
c = str()
for c in line:
if c >= "a" and c <= "z":
x = ord(c)
x += 23
x %= 26
y = chr(x)
output = output + y
if c >= "A" and c <= "Z":
x = ord(c)
x += 23
x %= 26
y = chr(x)
output = output + y
else:
output = output + c
outf.write(output + "\n")
I appreciate any help or advice you can offer! Thanks!!
EDIT:
Sample Input: "Wkh Orug Ri Wkh Ulqjv:"
Output: " :"
Expected Output: "The Lord Of The Rings:"
The ord() method in Python converts a character into its Unicode code value. Capital 'A' starts from unicode 65 and small 'a' from 97. Also you should use 'elif' instead of second 'if' statement. Otherwise, in case of a small letter, the letter will repeat. You need to get the letters position in alphabet, apply the shift and convert it back to unicode. A working example of your code is given below:
inpf = open("input.txt",'r')
outf = open("output.txt",'w')
decode = inpf.readlines()
for line in decode:
output = ""
c = str()
for c in line:
if c >= "a" and c <= "z":
x = ord(c) - 97 #small a
x -= 3
x = (x+26)%26 #For handling negative numbers
y = chr(x + 97)
output = output + y
elif c >= "A" and c <= "Z":
x = ord(c) - 65 #Capital A
x -= 3
x = (x+26)%26 #For handling negative numbers
y = chr(x + 65)
output = output + y
else:
output = output + c
outf.write(output + "\n")

Advice required in understanding the logic/maths in encryption program

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).

Shifting character in a list

I wanted to shift character with some specified position Ex :Input = ['banana', 7] here 7 is the step to move the position. Output = utgtgt.
It should work with any user input. User input can be lower and upper case.
numeric value is the number of position need to shift
Code:
input_list = ['banana', 7]
message=input_list[0]
n=input_list[1]
list1=[]
for i in message:
ch = i
x = chr(ord(ch)-n)
list1.append(x)
print("".join(list1))
You should calculate the offset of a given character to the ordinal number of 'a', shift it by n, get its modulo of 26, and then get the ordinal number of the shifted character by adding the ordinal number of 'a' back:
Change:
x = chr(ord(ch)-n)
to:
x = chr((ord(ch) - ord('a') - n) % 26 + ord('a'))
To handle uppercase letters as well, you can conditionally set the base character first:
base = ord('a' if ch.islower() else 'A')
x = chr((ord(ch) - base - n) % 26 + base)
Demo: https://repl.it/#blhsing/ClosedElectricEquation
The lowercase letters only occupy codepoints 97 through 122. You have to wrap around if ord(ch) - n is less than 97.
for ch in message:
new = ord(ch) - n
if new < ord('a'):
new += 26
list1.append(chr(new))
Building on top #blhsing answer you could get your output in one line with list comprehensions.
"".join([chr((ord(ch) - ord('a') - n) % 26 + ord('a'))for ch in message])
>>> 'utgtgt'

Yet another caesar cipher in python

I'm writing a function to shift text by 13 spaces. The converted chars need to preserve case, and if the characters aren't letters then they should pass through unshifted. I wrote the following function:
def rot13(str):
result = ""
for c in str:
if 65 <= ord(c) <= 96:
result += chr((ord(c) - ord('A') + 13)%26 + ord('A'))
if 97 <= ord(c) <= 122:
result += chr((ord(c) - ord('a') + 13)%26 + ord('a'))
else:
result += c
print result
What I have found is that lowercase letters and non-letter characters work fine. However, when the function is applied to uppercase chars the function returns the shifted char FOLLOWED BY the original char. I know there are plenty of solutions to this problem on SO, but this specific error has me wondering what's wrong with my logic or understanding of chars and loops in python. Any help appreciated.
You are missing the "else" statement, so if the first if "fires" (c is an uppercase letter) then the "else" from the second if also "fires" (and concatenates the uppercase letter, as ord(c) is not between 97 and 122)
def rot13(str):
result = ""
for c in str:
if 65 <= ord(c) <= 96:
result += chr((ord(c) - ord('A') + 13)%26 + ord('A'))
elif 97 <= ord(c) <= 122:
result += chr((ord(c) - ord('a') + 13)%26 + ord('a'))
else:
result += c
print result
Also, uppercase characters end with ord('Z')==90, ASCII characters between 91 and 96 are not letters. Function should also return the value, not print it (unless it is called print_rot13). Your function is also inconsistent - you use ord('A') in calculations, but actual, hard-coded value in if (65) you should decide on one of these.
def rot13(str):
a = ord('a')
z = ord('z')
A = ord('A')
Z = ord('Z')
result = ""
for c in str:
symbol = ord(c)
if A <= symbol <= Z:
result += chr((symbol - A + 13)%26 + A)
elif a <= symbol <= z:
result += chr((symbol - a + 13)%26 + a)
else:
result += symbol
return result
This way it only assume, that lower and upper case letters are arranged in consistent blocks, but nothing about their actual ord values.
This is an (rather contrived) one-liner implementation for the caesar cipher in python:
cipher = lambda w, s: ''.join(chr(a + (i - a + s) % 26) if (a := 65) <= (i := ord(c)) <= 90 or (a := 97) <= i <= 122 else c for c in w)
word = 'Hello, beautiful World!'
print(cipher(word, 13)) # positive shift
# Uryyb, ornhgvshy Jbeyq!
word = 'Uryyb, ornhgvshy Jbeyq!'
print(cipher(word, -13)) # -13 shift means decipher a +13 shift
# Hello, beautiful World!
It rotates only ascii alpha letters, respects upper/lower case, and handles positive and negative shifts (even shifts bigger that the alphabet).
More details in this answer.

Categories