patternpythonMinor
Encryption and decryption using alphabetic shifts
Viewed 0 times
encryptionshiftsdecryptionalphabeticusingand
Problem
My first piece of code encrypts text by moving each letter in the string 5 letters across in the alphabet.
The output of this code if I enter 'hello' would be 'mjqqt'.
To decrypt, I do the reverse, I move each letter 5 letters down the alphabet.
Below is my second piece of code that encrypts text against a code word. For example...
If I had 'abc' for the codeword and 'bcd' for the text to encrypt. Then: (a + b = 1 + 2) and this is repeated for each letter in the two words. The output of having my codeword as 'abc' and the word to encrypt and 'bcd' would be 'ceg'.
encrypt = input('Enter text to encrypt : ')
encrypt = encrypt.lower().replace(" ", "")
for i in encrypt:
print(chr(ord(i) + 5))
decrypt = input('Enter text to decrypt : ')
decrypt = decrypt.lower().replace(" ", "")
for i in decrypt:
print(chr(ord(i) - 5))The output of this code if I enter 'hello' would be 'mjqqt'.
To decrypt, I do the reverse, I move each letter 5 letters down the alphabet.
Below is my second piece of code that encrypts text against a code word. For example...
If I had 'abc' for the codeword and 'bcd' for the text to encrypt. Then: (a + b = 1 + 2) and this is repeated for each letter in the two words. The output of having my codeword as 'abc' and the word to encrypt and 'bcd' would be 'ceg'.
codeword = input('Enter codeword : ')
codeword = codeword.replace(" ", "")
encrypt = input('Enter text to encrypt : ')
encrypt = encrypt.replace(" ", "")
j = 0
for i in codeword:
print(chr(ord(encrypt[j])+ ord(codeword[j])-96))
j+=1Solution
Part 1, Caesar cipher
Instead of printing one character at a time (inefficient), create a string using the
The variable name
Separate the encryption/decryption into a function
Rather than forcing printing text to screen, a function returning a string can be used in any other context as well. It also lets us use the similarity between encryption and decryption to reduce repetition in the code.
The number of places to shift is a parameter instead of hardcoded, with a default of 5 to work the same as your example when the argument is omitted. A value of -5 then decrypts the resulting string.
Handle out-of-range characters
The technique appears to be meant to only work on (English) letters. To avoid unintended results on different inputs, one option is to leave other characters unmodified.
Also, "wrap around" instead of substituting for characters outside of the alphabet, by using the modulus operator
With the character substitution logic now being too long to fit comfortably in one line, I chose to separate it into a nested function.
Part 2, Vigenère cipher
Loop variables
Your loop here is somewhat confusing, in that the actual loop variable
An even better, more idiomatic way to loop over multiple sequences (strings, lists etc.) is with the built-in
Write as a function
Let's also apply the advice from the previous part and write it as a function.
Notice how it can be implemented using the
Handle different text and key lengths
What if the text to be encrypted or decrypted and the key are of different lengths? The convention is to repeat the key phrase.
While it wouldn't be too hard to write code for this from the beginning, it's often a good idea to look for something useful in the standard library. In this case, the
I've left out the input and output parts from this answer.
Instead of printing one character at a time (inefficient), create a string using the
str.join method.''.join(chr(ord(char) + 5) for char in text)The variable name
i is associated with integer indexes and is misleading to use for a character.Separate the encryption/decryption into a function
Rather than forcing printing text to screen, a function returning a string can be used in any other context as well. It also lets us use the similarity between encryption and decryption to reduce repetition in the code.
def caesar_shift(text, places=5):
text = text.lower().replace(' ', '')
return ''.join(chr(ord(char) + places) for char in text)The number of places to shift is a parameter instead of hardcoded, with a default of 5 to work the same as your example when the argument is omitted. A value of -5 then decrypts the resulting string.
Handle out-of-range characters
The technique appears to be meant to only work on (English) letters. To avoid unintended results on different inputs, one option is to leave other characters unmodified.
Also, "wrap around" instead of substituting for characters outside of the alphabet, by using the modulus operator
%.from string import ascii_lowercase
def caesar_shift(text, places=5):
def substitute(char):
if char in ascii_lowercase:
char_num = ord(char) - 97
char = chr((char_num + places) % 26 + 97)
return char
text = text.lower().replace(' ', '')
return ''.join(substitute(char) for char in text)With the character substitution logic now being too long to fit comfortably in one line, I chose to separate it into a nested function.
Part 2, Vigenère cipher
Loop variables
Your loop here is somewhat confusing, in that the actual loop variable
i goes unused while an additional variable j is used as the index. It could be written as follows to avoid that particular issue (using min in case the lengths are different).for i in range(min(len(text), len(key))):
print(chr(ord(text[i]) + ord(key[i]) - 96))An even better, more idiomatic way to loop over multiple sequences (strings, lists etc.) is with the built-in
zip function, which creates a tuple of elements for every loop iteration.for text_char, key_char in zip(text, key):
print(chr(ord(text_char) + ord(key_char) - 96))Write as a function
Let's also apply the advice from the previous part and write it as a function.
def vigenere(text, key):
return ''.join(caesar_shift(tc, ord(kc)-96) for tc, kc in zip(text, key))Notice how it can be implemented using the
caesar_shift function from above.Handle different text and key lengths
What if the text to be encrypted or decrypted and the key are of different lengths? The convention is to repeat the key phrase.
While it wouldn't be too hard to write code for this from the beginning, it's often a good idea to look for something useful in the standard library. In this case, the
itertools.cycle function turns out to be convenient.from itertools import cycle
def vigenere(text, key):
key = cycle(key)
return ''.join(caesar_shift(tc, ord(kc)-96) for tc, kc in zip(text, key))I've left out the input and output parts from this answer.
Code Snippets
''.join(chr(ord(char) + 5) for char in text)def caesar_shift(text, places=5):
text = text.lower().replace(' ', '')
return ''.join(chr(ord(char) + places) for char in text)from string import ascii_lowercase
def caesar_shift(text, places=5):
def substitute(char):
if char in ascii_lowercase:
char_num = ord(char) - 97
char = chr((char_num + places) % 26 + 97)
return char
text = text.lower().replace(' ', '')
return ''.join(substitute(char) for char in text)for i in range(min(len(text), len(key))):
print(chr(ord(text[i]) + ord(key[i]) - 96))for text_char, key_char in zip(text, key):
print(chr(ord(text_char) + ord(key_char) - 96))Context
StackExchange Code Review Q#120223, answer score: 6
Revisions (0)
No revisions yet.