I am working on a program in Python that sort-of encrypts a message. I have converted the string (which will vary based on user input) to a list and have a number. I want to automatically change each letter in the list with the number. For example, if the number is three, every A changes into a D. I have a dictionary with the vales for letters, such as {"a" : 1}, {"b" : 2}, etc.
I can not seem to get how I could change the letters (without knowing what they are) and possibly in accordance with my dictionary.
What I have so far (dictionary is somewhere else):
def numtolist(n):
seedstring = str(n)
numlist = []
for digit in seedstring:
numlist.append(int(digit))
return numlist
currentnumber = seed^2
newmessage = str()
for letter in messageList:
numtolist(currentnumber)
num1 = numlist[0]
If the transform is as simple as an alphabetic shift you can simply do it by:
if original_character.isupper(): # Determine whether it is upper/lower case
base_a = 'A' # use 'A' as a base for uppercase
else: #
base_a = 'a' # use 'a' as a base for lowercase
orig_char_ascii = ord(original_character) # Get the original ascii value
orig_char_alpha_index = orig_char_ascii - ord(base_a) # Get the `0-25` alphabetic
# index of the character
shift_number = 3 # Set the amount to shift by
new_char_alpha_index = orig_char_alpha_index + shift_number # Add the shift value
new_char_alpha_index = new_char_alpha_index % 26 # Take the modulus to impose
# periodic boundary conditions
# i.e. if you had 'z' + 3
# 'z' + 3 -> 25 + 3 -> 28
# 28 % 26 = 2, 2 -> 'c'
new_char_ascii_index = ord(base_a) + new_char_alpha_index # scale back to the ascii
# value
new_char = chr(new_char_ascii_index)
The basic idea is that each character corresponds to an ascii number which can be gotten by ord (i.e. ord('a') = 97). The chr method reverses this: chr(97) = 'a'.
The method is briefly: you get the ascii value, scale to a 0-25 alphabetic range, add your shift, wrap values that overflow the alphabetic range, scale back to ascii, and get a character back via chr.
You can compact this method a lot, I was verbose in the name of education:
def shift_char(ch,shift):
if not ch.isalpha():
return ch # return non-alphabetic characters as is
is_upper = 'A' <= original_character <= 'Z'
if is_upper:
base_a = 'A'
else:
base_a = 'a'
return chr(ord(base_a) + (ord(ch)-ord(base_a)+shift)%26)
If you wanted to use the same random shift for a whole string you could split this into a couple methods:
def shift_char(ch,shift):
if not ch.isalpha():
return ch # return non-alphabetic characters as is
if ch.isupper():
base_a = 'A'
else:
base_a = 'a'
return chr(ord(base_a) + (ord(ch)-ord(base_a)+shift)%26)
def shift_string(s,shift):
return ''.join(shift_char(i,r) for i in s)
If you want to be able to decode it later you need a reversal method and to store the shift value:
def shift_char(ch,shift):
if not ch.isalpha():
return ch # return non-alphabetic characters as is
if ch.isupper():
base_a = 'A'
else:
base_a = 'a'
return chr(ord(base_a) + (ord(ch)-ord(base_a)+shift)%26)
def encode_string(s,shift):
return ''.join(shift_char(i,r) for i in s), shift # NOTE EXTRA return value
def decode_string(s,shift):
return ''.join(shift_char(i,-shift) for i in s)
# usage
s = 'Hello, StackOverflow!'
enc_s,shift = encode_string(s)
dec_s = decode_string(enc_s,shift)
If your shift is random you can just pass that as the argument:
import random
def shift_char(ch,shift):
if not ch.isalpha():
return ch # return non-alphabetic characters as is
if ch.isupper():
base_a = 'A'
else:
base_a = 'a'
return chr(ord(base_a) + (ord(ch)-ord(base_a)+shift)%26)
def encode_string(s,shift):
return ''.join(shift_char(i,shift) for i in s)
def decode_string(s,shift):
return ''.join(shift_char(i,-shift) for i in s)
# usage
s = 'Hello, StackOverflow!'
shift = random.randint(1,25)
enc_s = encode_string(s,shift)
dec_s = decode_string(enc_s,shift)
or rearrange the methods
import random
def shift_char(ch,shift):
if not ch.isalpha():
return ch # return non-alphabetic characters as is
if ch.isupper():
base_a = 'A'
else:
base_a = 'a'
return chr(ord(base_a) + (ord(ch)-ord(base_a)+shift)%26)
def encode_string(s):
shift = random.randint(1,25)
return ''.join(shift_char(i,shift) for i in s), shift
def decode_string(s,shift):
return ''.join(shift_char(i,-shift) for i in s)
# usage
s = 'Hello, StackOverflow!'
enc_s, shift = encode_string(s)
dec_s = decode_string(enc_s,shift)
Related
So Ive been working on a code to convert hexadecimal to ASCII then sort the letters with selection sort. But when I get a input such as HBbmYa, it outputs as B,H,Y,a,b,m. It should output as a,B,b,H,m,Y or a,B,b,H,m,y. Is there a way to solve this problem? Here is the code i have right now.
#ask for input
hex_str = input("Write hexadecimal string here without spaces: ")
#format the input so it doesn't have any spaces or commas for the code to work
hex_str = hex_str.replace("," , "")
hex_str = hex_str.replace(" " , "")
#convert input to ASCII
def hexToASCII(hexx):
# initialize the ASCII code string as empty.
asci = ""
for i in range(0, len(hexx), 2):
# extract two characters from hex string
part = hexx[i : i + 2]
# change it into base 16 and
# typecast as the character
ch = chr(int(part, 16))
# add this char to final ASCII string
asci += ch
return asci
#function call
ascii_output = hexToASCII(hex_str)
# print the ASCII string.
print("ASCII output is: {}".format(ascii_output))
def selectionSort(u):
sortedarry = []
def findSmallest(l):
x = l[0]
for i in l:
[ascii_output.lower() for ascii_output in u]
if i < x:
x = i
return l.index(x)
while len(u) > 0:
x = findSmallest(u)
sortedarry.append(u.pop(x))
return sortedarry
u = list(ascii_output)
sortedarry = selectionSort(u)
# print the sorted array
print("The sorted array is: {}".format(sortedarry))
In your findSmallest function, you could use a case insensitive comparison by lowercasing the two elements you compare:
def findSmallest(l):
x = l[0]
for i in l:
if i.lower() < x.lower():
x = i
return l.index(x)
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)
I struck a brick wall trying to solve this and I am not sure how to approach this problem.
My idea is to compare both first characters of each string and if they are the same, save the character in the alphabet string of the index position shift.
Recurse the rest of the string by removing the first character of the secret. If the first characters are different, recurse but removing the first character of the alphabet string.
I am not sure how to recurse for the rest of the alphabet though.
alphabet = "abcdefghijklmnopqrstuvwxyz"
def caesar_encrypt(secret, shift):
if len(secret) == 0:
return ""
elif shift == 0:
return secret
else:
if secret[0] == alphabet[0]:
return alphabet[shift] + caesar_encrypt(secret[1:],shift)
else:
return caesar_encrypt(secret,shift), alphabet[1:]
I would suggest using str.index to find the index of where each character is in the alphabet for each character in your string. After, use this to index into the alphabet and recurse.
A couple of gotchas that you should look out for:
If the string contains a space, place this space as is in the string then move onto the next character
You need to handle wraparound where if we're at the end of the alphabet and you choose a shift that would exceed the end of the alphabet, you need to wrap around and go to the beginning of the alphabet.
This should work:
alphabet = "abcdefghijklmnopqrstuvwxyz"
def caesar_encrypt(secret, shift):
if len(secret) == 0:
return ""
elif shift == 0:
return secret
elif secret[0] == ' ': # New - handle spaces
return ' ' + caesar_encrypt(secret[1:], shift)
else:
index = (alphabet.index(secret[0]) + shift) % len(alphabet) # Find the right place to access the alphabet
return alphabet[index] + caesar_encrypt(secret[1:], shift) # Use this new character then recurse
NB: This implementation only handles lowercase letters.
How about this:
def shift_alphabet(shift):
return alphabet[shift:] + alphabet[:shift]
def caesar_encrypt(secret, shift):
coded_alphabet = shift_alphabet(shift)
coded = [coded_alphabet[alphabet.index(i)] for i in secret]
coded = ''.join(coded)
return coded
Using map/lambda instead of for:
def shift_alphabet(shift):
return alphabet[shift:] + alphabet[:shift]
def encrypt_letter(letter, coded_alphabet):
return coded_alphabet[alphabet.index(letter)]
def caesar_encrypt(secret, shift):
coded_alphabet = shift_alphabet(shift)
coded = map(lambda x: encrypt_letter(x, coded_alphabet), secret)
coded = ''.join(coded)
return coded
Using this program to take out spaces, punctuation, and make letters lower case...
def pre_process(s):
s= s.replace("'","")
s= s.replace('.','')
s= s.lower()
s= s.replace(" ","")
return s
How can I encrypt a message (s) so that the letters each shift by an amount equal to the corresponding letter in the alphabet? ex) 'm' shifted 5 becomes 'r' but 'w' shifted 5 becomes 'b'?
You have to do some ord and chr tricks to do what you want. Basically, these two functions return the integer representation of your letters, and outputs the corresponding letter of the integer representation, respectively.
def pre_process(s):
s= s.replace("'","")
s= s.replace('.','')
s= s.lower()
s= s.replace(" ","")
mystring = ""
for letter in s:
shift = 5
r = ord(letter)+shift
if (r > ord('z')):
r -= 26
mystring += (chr(r))
return mystring
This may be helpful to you...Just change the value of shifter in encrypt function to get the corresponding shift
def shift(char,shift_by):
val=ord(char) #gives ascii value of charecter
val+=shift_by
if(val>122):
val=97+(val-122-1)
return(chr(val))
def encrypt_string(str):
shifter=2 #change to value of shifter here
encrypted_string=""
for i in str :
if( (ord(i)>=97) and (ord(i)<=122) ):
i=shift(i,shifter)
encrypted_string+=i
return(encrypted_string)
there is more elegant (pythonic + effective) way to find word on given position?
FIRST_WORD = re.compile(r'^(\w+)', re.UNICODE)
LAST_WORD = re.compile(r'(\w+)$', re.UNICODE)
def _get_word(self, text, position):
"""
Get word on given position
"""
assert position >= 0
assert position < len(text)
# get second part of word
# slice string and get first word
match = FIRST_WORD.search(text[position:])
assert match is not None
postfix = match.group(1)
# get first part of word, can be empty
# slice text and get last word
match2 = LAST_WORD.search(text[:position])
if match2 : prefix = match2.group(1)
else : prefix = ''
return prefix + postfix
# | 21.
>>> _get_word("Hello, my name is Earl.", 21)
Earl
>>> _get_word("Hello, my name is Earl.", 20)
Earl
Thanks
Here's how I'd do it:
s = "Hello, my name is Earl."
def get_word(text, position):
words = text.split()
characters = -1
for word in words:
characters += len(word)
if characters > = position:
return word
>>> get_word(s, 21)
Earl.
Stripping off the punctuation can be done with ''.strip() or regular expressions or something hacky like
for c in word:
final += c if c.lower() in 'abcdefghijklmnopqrstuvwxyz'
import string
s = "Hello, my name is Earl."
def get_word(text, position):
_, _, start = text[:position].rpartition(' ')
word,_,_ = text[position:].partition(' ')
return start+word
print get_word(s, 21).strip(string.punctuation)
The following solution is to get the alpha characters around the given position:
def get_word(text, position):
if position < 0 or position >= len(text):
return ''
str_list = []
i = position
while text[i].isalpha():
str_list.insert(0, text[i])
i -= 1
i = position + 1
while text[i].isalpha():
str_list.append(text[i])
i += 1
return ''.join(str_list)
The following is a test case:
get_word("Hello, my name is Earl.", 21) # 'Earl'
get_word("Hello, my name is Earl.", 20) # 'Earl'
I don't think it is a good idea to split the text into words with the split function here, because position is essential for this problem. If there are continuous blanks in a text, the split function may cause troubles.