patternpythonMinor
One-time pad encryption using letter shifting
Viewed 0 times
encryptiontimeoneusingletterpadshifting
Problem
This program asks the user to input a List of words. It then returns a list of encrypted words, and a second list which specifies the numerical shifts used to encrypt those words. The idea was obtained from https://www.khanacademy.org/computing/computer-science/cryptography/crypt/v/one-time-pad
import random
def rotate_letter(letter, number):
if letter == ' ':
return letter
if letter.islower():
start = ord('a')
elif not letter.islower():
start = ord('A')
normal_shift = ord(letter) - start
new_letter = start + (normal_shift + number) % 26
return chr(new_letter)
def avoid(letter):
avoid = ' ,!?¿,*+-%/@1234567890'
for i in avoid:
if i == letter:
return False
return True
def rotate_word(word):
new_word = ''
random_series = []
for letter in word:
if avoid(letter):
random_number = random.randint(1,26)
new_letter = rotate_letter(letter, random_number)
new_word += new_letter
random_series.append(random_number)
else:
new_word += letter
return (new_word, random_series)
def shift(words):
index = 0
encrypted_words = []
random_shifts = []
while index < len(words):
(new_word, random_series) = rotate_word(words[index])
encrypted_words.append(new_word)
random_shifts.append(random_series)
index += 1
return (encrypted_words, random_shifts)
def get_input():
words = []
while True:
user = (input('Input words to encrypt. Done when ready \n' ))
if user == 'Done' or user == 'done':
return words
words.append(user)
def start():
words = get_input()
encrypted_words = []
random_shifts = []
(encrypted_words, random_shifts) = shift(words)
print('UNENCRYPTED WORDS', words)
print('ENCRYPTED WORDS', encrypted_words)
print('RANDOM SHIFTS', random_shifts)
start()Solution
Your function
Whenever you are doing string addition in Python, you are probably doing something wrong. This is because strings are immutable so for every addition a new string has to be created and the content of the strings to be added needs to be copied. This is quite slow. Rather, use a
When returning a
When iterating over
Using
You should call your code with a
Final code:
avoid is quite unnecessary and also confusing. I would expect avoid('@') to return True. When you store the letters as a global constant and as set, you should gain some speed (both because it does not have to be reconstructed again and again and because membership testing is very fast for a set. I replaced your function with letter not in letters_avoid.Whenever you are doing string addition in Python, you are probably doing something wrong. This is because strings are immutable so for every addition a new string has to be created and the content of the strings to be added needs to be copied. This is quite slow. Rather, use a
list to accumulate the letters of new_word and str.join them at the end.When returning a
tuple, the () are implicit. You can just write return a, b.When iterating over
words, you should just directly iterate over it, instead of its indices.Using
str.lower cuts down the double comparison if user == 'Done' or user == 'done' to if user.lower() == 'done':.You should call your code with a
if __name__ == '__main__': guard to allow importing your functions from another script without executing the whole script.Final code:
import random
letters_avoid = set(' ,!?¿,*+-%/@1234567890')
def rotate_letter(letter, number):
if letter == ' ':
return letter
if letter.islower():
start = ord('a')
elif not letter.islower():
start = ord('A')
normal_shift = ord(letter) - start
new_letter = start + (normal_shift + number) % 26
return chr(new_letter)
def rotate_word(word):
new_word = []
random_series = []
for letter in word:
if letter not in letters_avoid:
random_number = random.randint(1, 26)
new_word.append(rotate_letter(letter, random_number))
random_series.append(random_number)
else:
new_word.append(letter)
return "".join(new_word), random_series
def shift(words):
encrypted_words = []
random_shifts = []
for word in words:
new_word, random_series = rotate_word(word)
encrypted_words.append(new_word)
random_shifts.append(random_series)
return encrypted_words, random_shifts
def get_input():
words = []
while True:
user = input('Input words to encrypt. Done when ready \n')
if user.lower() == 'done':
return words
words.append(user)
def start():
words = get_input()
encrypted_words, random_shifts = shift(words)
print('UNENCRYPTED WORDS', words)
print('ENCRYPTED WORDS', encrypted_words)
print('RANDOM SHIFTS', random_shifts)
if __name__ == "__main__":
start()Code Snippets
import random
letters_avoid = set(' ,!?¿,*+-%/@1234567890')
def rotate_letter(letter, number):
if letter == ' ':
return letter
if letter.islower():
start = ord('a')
elif not letter.islower():
start = ord('A')
normal_shift = ord(letter) - start
new_letter = start + (normal_shift + number) % 26
return chr(new_letter)
def rotate_word(word):
new_word = []
random_series = []
for letter in word:
if letter not in letters_avoid:
random_number = random.randint(1, 26)
new_word.append(rotate_letter(letter, random_number))
random_series.append(random_number)
else:
new_word.append(letter)
return "".join(new_word), random_series
def shift(words):
encrypted_words = []
random_shifts = []
for word in words:
new_word, random_series = rotate_word(word)
encrypted_words.append(new_word)
random_shifts.append(random_series)
return encrypted_words, random_shifts
def get_input():
words = []
while True:
user = input('Input words to encrypt. Done when ready \n')
if user.lower() == 'done':
return words
words.append(user)
def start():
words = get_input()
encrypted_words, random_shifts = shift(words)
print('UNENCRYPTED WORDS', words)
print('ENCRYPTED WORDS', encrypted_words)
print('RANDOM SHIFTS', random_shifts)
if __name__ == "__main__":
start()Context
StackExchange Code Review Q#151916, answer score: 2
Revisions (0)
No revisions yet.