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.
Related
Write a function
shiftLetter(letter, n)
whose parameter, letter should be a single character. If the character is between "A" and "Z", the function returns an upper case character 𝑛
n
positions further along, and "wrapping" if the + 𝑛
n
mapping goes past "Z". Likewise, it should map the lower case characters between "a" and "z". If the parameter letter is anything else, or not of length 1, the function should return letter.
Hint: review functions ord() and chr() from the section, as well as the modulus operator %.
Down below is what I worked so far. It should return to A after alphabet is over, but it's not working properly from alphabet x. I guess..? I should subtract 90(Z) - 65(A) in ASCII table for x,y,z but confused about that.
def shiftLetter(letter,n):
if letter >= 'A' and letter <= 'Z':
return chr(ord(letter) + n )
elif letter >= 'a' and letter <= 'z':
return chr(ord(letter) + n )
print(shiftLetter('w',3))
You can use the mod operator to wrap the alphabet:
def shiftLetter(letter,n):
if letter >= 'A' and letter <= 'Z':
return chr((ord(letter) - ord('A') + n) % 26 + ord('A') )
elif letter >= 'a' and letter <= 'z':
return chr((ord(letter) - ord('a') + n) % 26 + ord('a') )
print(shiftLetter('w',3)) # z
print(shiftLetter('E',3)) # H
print(shiftLetter('Y',3)) # B
print(shiftLetter('f',26)) # f
print(shiftLetter('q',300)) # e
Output
z
H
B
f
e
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
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'
Hi all :) I'm building a script to code text with a simple text rotation(ROT).
The script works well, but I'm having a problem, it also rotates all symbols like [spaces,!,?,.] I'm working with ascii table to do it, what can i do to avoid rotating that type of characters?
def rot13(input,key): #Function to code a text with caeser chyper.
if key > 25:
key = 25
elif key < 2:
key = 2
finaltext = ''
for letter in input:
num = ord(letter)
if (num + key) > 122: #If the final number is greater than 122..
x = (num + key) - 122
finaltext += chr(x + ord('a') - 1)
elif((num + key <= 122)):
finaltext += chr(num + key)
print(finaltext)
before "rotating" your character, add a check to see whether or not it is alphanumeric:
if letter.isalpha():
# Do your thing
else:
finaltext += letter
Try this:
>>> import string
>>> letter = 'a'
>>> letter in string.letters
True
>>> letter = '.'
>>> letter in string.letters
False
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)