Caesar Shift on list elements using list comprehension - python

I am very new to Python and have run into a road block. Is it possible to use list comprehension to perform a shift on each letter of a word in a list? Also how could I utilize ord() and chr() in a similar list comprehension manner?
So far I have the following code:
def shift( file1="file1.txt", file2 ="file2.txt"):
key = int(input("Enter shift key: "))
with open(" file1. txt") as readfile:
lines = readfile.readlines()
lines = [words.lower() for words in lines]
lines = [ words.split(" ") for words in lines]
All I need now is to perform the actual shift but I am stumped :/

Here is a simple Caesar Shift using a comprehension:
>>> string = 'CaesarShift'; shift=3
>>> ''.join(chr(ord('a') + (ord(c)-ord('a')+shift) % 26) for c in string)
'zdhvdupkliw'
This illustrates the concept but makes no attempt to handle spaces or punctuation.
Reversibility
>>> new = ''.join(chr(ord('a') + (ord(c)-ord('a')+shift) % 26) for c in string.lower())
>>> ''.join(chr(ord('a') + (ord(c)-ord('a')-shift) % 26) for c in new)
'caesarshift'

You can use str.join , iterating over each ch/character in each word from the word_list, you can use whatever formula you have to create your cipher.
word_list = ["Foo","Bar","Foobar"]
print(["".join(chr(ord(ch) + 10) for ch in word.lower()) for word in word_list ])
['pyy', 'lk|', 'pyylk|']

Wrap Around: The Caesar Shift is a wrap-around shift cipher, so you have to have an algorithm to wrap around strings.
If you consider letters as numbers, then you could write the letters out as [0, 1 ... 25], i.e. range(26).
If you do a Caesar shift of 10 on this, you will get: [10, 11 ... 25, 26 ... 35].
Character 26 isn't in the alphabet. You will need to change this to 0. Then change character 27 to 1, and so on. So, the transformation you are looking for, (if the alphabet was arranged from 0 to 25) is, mod( letterValue + 10, 26).
However, the letters do not start from 0, so you will have to first subtract the value of ord('a') and then add it later.
The letterValue in the above expression is simply: ord(ch) - ord('a'). So change the earlier expression to (chr(ch) - ord('a') + 10) % 26.
Then change it back using: chr((chr(ch) - ord('a') + 10) % 26 + ord('a')).
Since ord('a') is 96, you can speed up this process a little by using: chr((chr(ch) - 96 + 10)%26 + 96), i.e., chr((chr(ch)-86)%26 + 96)
Non alphabetic characters: What will characters such as ? and ! translate to? These generally go through unaltered. You can supply that with an if condition and check if the requested character is in string.ascii_lowercase.
Something like:
from string import ascii_lowercase as lowerLetters
def toCaesar(ch):
if ch in lowerLetters:
return chr((chr(ch) - 86)%26 + 96)
else:
return ch
The rest I think you already have.

Related

Caesar Cipher shift by two letters

def main():
cc = (input("Enter Message to Encrypt\n"))#user input
shift = int(2) #shift length
a=["a","b","c","d","e","f","g","h","i","j","k","l",
"m","n","o","p","q","r","s","t","u","v","w","x","y","z"] #reference list
newa={} #new shifted reference list
for i in range (0,len(a)):
newa [a[i]]=a[(i+shift)%len(a)]
#adds shifted 2 alaphabet into newalaphabet
#% moodulus used to wrap
for i in cc: #iterates through cc
if i in a:
a[i]=cc[i]
a[i]=newa[i]
main()
So I need input from the user #cc
the shift needs to be two
I used an alphabet list
then shift the alphabet by two to create newa
but I do not know how to apply the new alphabet to my user's input
Use a dictionary to map inputs to outputs
shifted_a = a[-shift:] + a[:-shift]
cipher = {a[i]: shifted_a[i] for i in range(len(a))}
output = ''.join(cipher[char] for char in cc)
Iterate through the string cc and replace all the alphabets using the get method of newa. Characters that are not in the dictionary are left as is, by passing them as the default to newa.get when the key is missing:
newa = {}
for i, x in enumerate(a):
newa[x] = a[(i+shift) % len(a)]
encrypted_text = ''.join(newa.get(i, i) for i in cc)
Python's builtin enumerate can be used in place of range(len(a)) in this case where you need the items in a and their respective indices.
Use mapping for every char, then join them back to create the encrypted message:
''.join(map(lambda x: chr((ord(x) - 97 + shift) % 26 + 97) if x in alphabet else x, cc.lower()))
Integrate it like that:
import string
alphabet = string.ascii_lowercase
cc = input('Enter string to encode: ')
shift = 2 # could be any number
encrypted = ''.join(map(lambda x: chr((ord(x) - 97 + shift) % 26 + 97) if x in alphabet else x, cc.lower()))
cc.lower() for the letters to be all same case (to map using constant ord)
chr((ord(x) - 97 + shift) % 26 + 97) :
get the value of the number minus 97 (0 for a, 1 for b, etc.).
apply the shift (a turns to c, etc.).
modulate by 26 to prevent letters like z from exceeding (25 + 2 = 27, 27 % 26 = 1 = b).
add 97 to bring the letter back to ascii standard (97 for a, 98 for b, etc.)
if x in alphabet else x cover for signs that are not letter (if you want to ignore spaces and punctuation use if x in alphabet else '' instead).
I would just build transition table and use it to decode string.
import string
shift = 2
letters = string.ascii_lowercase + string.ascii_uppercase
transtable = str.maketrans({letters[i]: letters[(i + shift) % len(letters)]
for i in range(len(letters))})
cc = input('Enter string to encode: ')
print(cc.translate(transtable))
I'll throw my solution in there. It should be pretty clear how it works...
import string
index_lookup = {letter: index for index, letter in enumerate(string.ascii_lowercase)}
def caesar_letter(l, shift=2):
new_index = index_lookup[l] + shift
return string.ascii_lowercase[new_index % len(index_lookup)]
def caesar_word(s):
return ''.join([caesar_letter(letter) for letter in s])
I think the above is better for readability but if you're opposed to imports...
index_lookup = {chr(idx): idx - ord('a') for idx in range(ord('a'), ord('z')+1)}
...
In [5]: caesar_word('abcdefghijklmnopqrstuvwxyz')
Out[5]: 'cdefghijklmnopqrstuvwxyzab'

Python Simple Encryption program: How to loop Z back to A

I'm trying to make an encryption function that encrypts plaintext messages but the problem is that if i input a key too large that goes past 'Z' then it goes onto greater unicode values.
My code:
def encrypt(var1,var2):
var3 = ""
for i in range(0, len(var1)):
if ord(var1[i])>64 and ord(var1[i])<90:
var3=var3+chr((ord(var1[i])+var2))
elif ord(var1[i])+var2>90:
???
else:
continue
return(var3)
How do I get it to loop 'Z' back to 'A'. I think I have to make an if statement like this but I'm not sure what to put into it.
elif ord(var1[i])+var2>90:
???
Here is my one! Im using the modulus operator to wrap around every 26 numbers (the number of letter between a-z). I also handle upper on lowercase separately.
def encrypt(data, shift):
result = ''
for c in data:
c_num = ord(c)
# is the letter lower case a - z?
if (c_num >= ord('a')) and (c_num <= ord('z')):
# get the letter number from 0 - 26
c_num = c_num - ord('a')
# shift the number
c_num += shift
# wrap the number every 26 numbers
c_num = c_num % 26
# now increase a by the new amount
c_num += ord('a')
result += chr(c_num)
# is the letter upper case A - Z?
elif (c_num >= ord('A')) and (c_num <= ord('Z')):
# get the letter number from 0 - 26
c_num = c_num - ord('A')
# shift the number
c_num += shift
# wrap the number every 26 numbers
c_num = c_num % 26
# now increase a by the new amount
c_num += ord('A')
result += chr(c_num)
return result
encrypt('aAbB', 2)
'cCdD'
encrypt('afZz', 2)
'chBb'
Here is the code golf version using list comprehension just for fun!
def encrypt(data, shift):
return ''.join([chr(((ord(c) - ord('a') + shift) % 26) + ord('a')) if ord(c) in range(ord('a'), ord('z')+1) else chr(((ord(c) - ord('A') + shift) % 26) + ord('A')) for c in data])
A straight-forward way would be to check if you have passed beyond Z, and modify the character in that case:
...
if var1[i] >= 'A' and var1[i] <= 'Z':
translated_char = chr(ord(var1[i])+var2)
if translated_char > 'Z':
# If the resulting character is beyond Z,
# we go 26 characters back
translated_char = chr(ord(translated_char)-26)
# Append the translated character to the output string
var3 += translated_char
...
You may want to consider more descriptive variable names -- you'll thank yourself if you revisit your code after two months :-)
I would recommend using the modulus operator to do what you are wanting. In python that is the % character. In modulus math. X % Y tells us what the remainder of X / Y is. For example. 27 % 26 is 1. Using this you can get your wrap around that you want. Here is a sample bit of code to encrypt a single character
def encrypt_character( valToEncrypt, keyVal ):
# Update the character to be our standard Alphabet mapping
# A -> 0; B->1 ... Z -> 25
x = ord(valToEncrypt) - ord('A')
# Perform the Encryption
retVal = ( x + keyVal ) % 26
# Translate back to the standard ASCII mapping of the character
# for display in python and translate it back into a string
retVal = chr(retVal + ord('A'))
return retVal
# end encrypt_character
Now if we feed the character "A" Into our encryption algorithm with a key of 13 we get "N" as shown:
>>> encrypt_character("A", 13)
'N'
The decrypt algorithm is very similar, except you do subtraction instead of addtion
def decrypt_character( valToDecrypt, keyVal ):
# Update the character to be our standard Alphabet mapping
# A -> 0; B->1 ... Z -> 25
x = ord(valToDecrypt) - ord('A')
retVal = ( x - keyVal ) % 26
# Translate back to the standard ASCII mapping of the character
# for display in python and translate it back into a string
retVal = chr(retVal + ord('A'))
return retVal
To encrypt a string you can use the following function:
from re import sub
def encrypt_message( message, key ):
# Convert the message text into a plain text with all spaces and
# punctuation removed.
plainText = sub(r'[^A-Z]', '', message.upper())
cipherText = ""
charIndex = 0
# Encrypt the message 1 character at a time
while charIndex < len(plainText):
cipherText += \
encrypt_character( plainText[charIndex], key)
charIndex += 1
return cipherText
This function can be called:
>>> encrypt_message("HELLO World!", key=23)
'EBIILTLOIA'
The decryption function is very similar to the encryption function, except it calls the decrypt utility instead of the encrypt utility.

Adding certain numbers together in a string/list then turning then into letters

Currently doing a Vigenere cipher in Python, and me and many people in my class are stuck on one aspect.
After we translated the keyword to ordinals, we need to add those number to a message to encrypt it. This is my code so far.
Input = input('Enter your message: ')
key = input('Enter the one word key: ')
times = len(Input)//len(key)+1
encryptedKey = (times*key)[:len(Input)]
output = []
for character in Input:
number = ord(character) - 96
output.append(number)
outputKey = []
for character in encryptedKey:
numberKey = ord(character) - 96
outputKey.append(numberKey)
print(encryptedKey)
print(outputKey)
print(Input)
print(output)
So if the Input is 'hello', and the key is 'bye', the keyword would become 'byeby' [2,25,5,2,25], and 'hello' would be [8,5,12,12,15]. I can't figure out a way to add the first 2 with 8, the 25 with 5, and so on.
I tried print(sum(output + outputKey)) but of course that just adds all the numbers together, meaning the answer is 111.
I also need them to turn back into letters, so that it ends up with the encrypted message.
Thanks!
You're off to the right start. You've gotten your message and key translated into numbers.
keyphrase = [2,25,5,2,25]
message = [8,5,12,12,15]
Now you need to add them and modulo 26 so your answers are still a-z.
encrypted = [(keyphrase[i] + message[i])%26 for i in range(len(message))]
>>> encrypted
[10, 4, 17, 14, 14]
Now you need to turn those back into letters:
''.join(chr(c + 96) for c in encrypted)
'jdqnn'
And then you can recover the message by going the other way:
message = [(encrypted[i] - keyphrase[i])%26 for i in range(len(encrypted))]
>>> message
[8, 5, 12, 12, 15]
>>> ''.join(chr(c + 96) for c in message)
'hello'
A bit of an FYI, for computer cryptography, especially with a language like Python or C, it's usually standard to start counting at 0. So 'a' is 0, 'b' is 1, etc. You're starting at 1, which is okay, just be aware of it.
You could also try something like this (pseudocode):
int j = 0
for int i = 0; i < output.len(); i++
print output[i] + outputKey[j]
j++
if j > outputKey.len()
j = 0
That way, rather than extend your array from [b, y, e] to [b, y, e, b, y] you calculate the key once and use its index to loop over additional values as needed.

Decoding an encrypted file using Caesar Cipher

I want to decrypt an encrypted file. I'm having trouble all the way at the bottom when converting it and comparing it to a dictionary (which is full of words). Can someone guide me in the right direction? I'm struggling comparing the two.
#this function takes a string and encrypts ONLY letters by k shifts
def CaeserCipher(string, k):
#setting up variables to move through
upper = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'*10000
lower = 'abcdefghijklmnopqrstuvwxyz'*10000
newCipher = ''
#looping each letter and moving it k times
for letter in string:
if letter in upper:
if upper.index(letter) + k > 25:
indexPosition = (upper.index(letter) + k)
newCipher = newCipher + upper[indexPosition]
else:
indexPosition = upper.index(letter) + k
newCipher = newCipher + upper[indexPosition]
elif letter in lower:
if lower.index(letter) + k > 25:
indexPosition = (lower.index(letter) + k)
newCipher = newCipher + lower[indexPosition]
else:
indexPosition = lower.index(letter) + k
newCipher = newCipher + lower[indexPosition]
else:
newCipher = newCipher + letter
return newCipher
f = open('dictionary.txt', "r")
dictionary = set()
for line in f:
word = line.strip()
dictionary.add(word)
print dictionary
#main file
#reading file and encrypting text
f = open('encryptMystery1.txt')
string = ''
out = open("plain1.txt", "w")
myList = []
for line in f:
myList.append(line)
for sentence in myList:
for k in range(26):
updatedSentence = CaeserCipher(sentence, k)
for word in updatedSentence.split():
if word in dictionary:
out.write(updatedSentence)
break
print myList
f.close()
out.close()
Let's tackle this in steps, and the first step is entitled
WHY DO YOU HAVE 260,000 CHARACTER LONG STRINGS IN A CAESAR CIPHER
Sorry, I don't mean to be overly dramatic, but you realize that's going to take up more space than, well, Space, don't you? And it's completely unnecessary. It's an ugly and slow hack to avoid understanding the % (modulo) operator. Don't do that.
Now, to the modulo:
Step two of course will have to be understanding the modulo. It's not actually hard, it's just like the remainder of a division problem. You remember when you were in school and just LEARNING division? 7/4 was 1r3 not 1.75, remember? Well Python has functions for all that. 7/4 == 1.75, 7//4 == 1 and 7 % 4 == 3. This is useful because it can serve to "wrap" a number around a fixed length.
Let's say for example you have some string with 26 indexes (like, I don't know, an alphabet?). You're trying to add some number to a starting index, then return the result but UGH YOU'RE ADDING 2 TO Y AND IT DOESN'T WORK! Well with modulo it can. Y is in index 24 (remember zero is its own index), and 24+2 is 26 and there IS no 26th index. However, if you know there's going to be only 26 elements in your string, we can take the modulo and use THAT instead.
By that logic, index + CONSTANT % len(alphabet) will ALWAYS return the right number using simple math and not sweet baby jesus the quarter million element long string you just butchered.
Ugh your mother would be ashamed.
Reversing a Caesar cipher
So you've got a good idea, going through each line in turn and applying every kind of cipher to it. If I were you I'd dump them all into separate files, or even into separate list elements. Remember though that if you're reversing the cipher, you need to use -k not k. It's probably a good idea to simply change your Caesar cipher to detect that though, since the modulo trick doesn't work in this case. Try something like:
def cipher(text, k):
cipherkey = "SOMESTRINGGOESHERE"
if k < 0:
k = len(cipherkey) + k
# len(cipherkey) - abs(k) would be more clear, but if it HAS to be
# a negative number to get in here, it seems silly to add the call
# to abs
Then you can do:
startingtext = "Encrypted_text_goes_here"
possibledecrypts = [cipher(startingtext, -i) for i in range(1,26)]

Cesar Cipher on Python beginner level

''' Cesar Cipher '''
def encrypt(word, shift):
word = word.lower()
for i in word:
r = chr(ord(i)+shift)
if r > "z":
r = chr(ord(i) - 26 + shift)
word = word.replace(i, r)
return word
if __name__ == "__main__": print encrypt("programming", 3)
This gives me wrong answers on shifts higher than 1 and words longer then 2. I can't figure out why. Any help please?
Thilo explains the problem exactly. Let's step through it:
''' Cesar Cipher '''
def encrypt(word, shift):
word = word.lower()
for i in word:
r = chr(ord(i)+shift)
if r > "z":
r = chr(ord(i) - 26 + shift)
word = word.replace(i, r)
return word
Try encrypt('abc', 1) and see what happens:
First loop:
i = 'a'
r = chr(ord('a')+1) = 'b'
word = 'abc'.replace('a', 'b') = 'bbc'
Second loop:
i = 'b'
r = chr(ord('b')+1) = 'c'
word = 'bbc'.replace('b', 'c') = 'ccc'
Third loop:
i = 'c'
r = chr(ord('c')+1) = 'd'
word = 'ccc'.replace('c', 'd') = 'ddd'
You don't want to replace every instance of i with r, just this one. How would you do this? Well, if you keep track of the index, you can just replace at that index. The built-in enumerate function lets you get each index and each corresponding value at the same time.
for index, ch in enumerate(word):
r = chr(ord(ch)+shift)
if r > "z":
r = chr(ord(ch) - 26 + shift)
word = new_word_replacing_one_char(index, r)
Now you just have to write that new_word_replacing_one_char function, which is pretty easy if you know slicing. (If you haven't learned slicing yet, you may want to convert the string into a list of characters, so you can just say word[index] = r, and then convert back into a string at the end.)
I don't know how Python likes replacing characters in the word while you are iterating over it, but one thing that seems to be a problem for sure is repeated letters, because replace will replace all occurrences of the letter, not just the one you are currently looking at, so you will end up shifting those repeated letters more than once (as you hit them again in a later iteration).
Come to think of it, this will also happen with non-repeated letters. For example, shifting ABC by 1 will become -> BBC -> CCC -> DDD in your three iterations.
I had this assignment as well. The hint is you have to keep track of where the values wrap, and use that to your advantage. I also recommend using the upper function call so everything is the same case, reduces the number of checks to do.
In Python, strings are immutable - that is they cannot be changed. Lists, however, can be. So to use your algorithm, use a list instead:
''' Cesar Cipher '''
def encrypt(word, shift):
word = word.lower()
# Convert the word to a list
word = list(word)
# Iterate over the word by index
for i in xrange(len(word)):
# Get the character at i
c = word[i]
# Apply shift algorithm
r = chr(ord(c)+shift)
if r > "z":
r = chr(ord(c) - 26 + shift)
# Replace the character at i
word[i] = r
# Convert the list back to a string
return ''.join(word)
if __name__ == "__main__": print encrypt("programming", 3)

Categories