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
Related
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'
Is there a Function in Python to easily create a circular Alphabet, so that the letter after z is a and the letter before a is z?
I tried something with chr() and .join(Alphabet), but that didn't worked because i got the error message an integer is required (got type str).
for character in word:
if chr(Alphabet[Alphabet.find(character)) >= "z":
new_Alphabet = Alphabet.join(Alphabet)
elif chr(Alphabet[Alphabet.find(character)) <= "a":
new_Alphabet = Alphabet.join(Alphabet[:-1])
Use itertools.cycle ans string.ascii_lowercase:
from itertools import cycle
import string
circular_alphabet = cycle(string.ascii_lowercase)
That is an infinite iterator with the lowercase letters:
>>> "".join(next(circular_alphabet ) for _ in range(50))
'abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwx'
I think you have to use circular queue. for more information please check this link.
Alternative (old fashion?) solution:
def cycle_letter(ch,up=True):
upper = 'A' <= ch <= 'Z'
ch = ch.lower()
letters = 'abcdefghijklmnopqrstuvwxyz'
pos = letters.find(ch)
if pos < 0: return ch
length = len(letters)
pos += 1 if up else length-1
ans = letters[pos%length]
if upper: ans = ans.upper()
return ans
################################################################################
def cycle_string(s,up=True):
return ''.join(cycle_letter(ch,up) for ch in s)
################################################################################
if __name__ == '__main__': #Test
s = cycle_string('Hello, World!')
print(s)
s = cycle_string(s,False)
print(s)
In case it helps someone, this snippet shifts a word by the desired number of spaces. (e.g. shift_word('abcdef', 12) = 'opqrst' )
def shift_word(word: str, spaces: int) -> str:
first_ordinal = 97
last_ordinal = 122
alphabet_size = 26
return ''.join(chr((ord(letter) - last_ordinal - spaces - 1) % alphabet_size + first_ordinal) for letter in word)
It simply iterates over the word letter-by-letter, applies some modulo math to calculate the right "bucket" where the letter should fall, and makes sure the result is in the boundaries of ordinals 97-122 (letters a-z)
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
In a ceasar cipher I need it to be that my upper characters remain upper and that non letter character remain non letters/the same. I have it that I can work with lower letters.
Upper letters however are converted to lower and a different letter. Non-letter characters are converted to a lower letter as well. Upper letters must shift but remain upper. Non-letter characters must remain as non-letter character.
p = raw_input(("enter a word"))
n = input(("how many must it shift"))
a = 0
b = 0
c = 0
d = 0
for i in p:
if i.isupper():
a += 1
elif i.islower():
b += 1
elif i.isdigit():
c += 1
else:
d += 1
e = ""
for i in p:
if i == "":
e += i
else:
integerValue = ord(i)
integerValue-= 97
integerValue += n
integerValue %= 26
integerValue += 97
e += chr(integerValue)
print e
You can use i.isalpha() to check if the current character is a letter or not and you can use i.isupper() to check if the current letter is uppercase or not. When you convert the letter you will need to make the letter lowercase and then convert it back to upper. On top of those changes you have too many parenthesis for your inputs. I used raw_input since I'm using python 2.7. Your formatting is so off your code that's posted won't run due to indentation errors and your line if i == "" checks for an empty string instead of a space which I am assuming you were going for. All that said here is what I did to your code to try and keep it similar to what you had while cutting out extraneous bits.
p = raw_input("enter a word")
n = int(raw_input("how many must it shift"))
e = ''
for i in p:
if not i.isalpha():
e+=i
else:
integerValue = ord(i.lower())
integerValue-= 97
integerValue += n
integerValue %= 26
integerValue += 97
if i.isupper():
e += chr(integerValue).upper()
else:
e += chr(integerValue)
print e
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.