I have been playing with Python and came across a task from MIT, which is to create coded message (Julius Cesar code where for example you change ABCD letters in message to CDEF). This is what I came up with:
Phrase = input('Type message to encrypt: ')
shiftValue = int(input('Enter shift value: '))
listPhrase = list(Phrase)
listLenght = len(listPhrase)
ascii = []
for ch in listPhrase:
ascii.append(ord(ch))
print (ascii)
asciiCoded = []
for i in ascii:
asciiCoded.append(i+shiftValue)
print (asciiCoded)
phraseCoded = []
for i in asciiCoded:
phraseCoded.append(chr(i))
print (phraseCoded)
stringCoded = ''.join(phraseCoded)
print (stringCoded)
The code works but I have to implement not shifting the ascii value of spaces and special signs in message.
So my idea is to select values in list in range of range(65,90) and range(97,122) and change them while I do not change any others. But how do I do that?
If you want to use that gigantic code :) to do something as simple as that, then you keep a check like so:
asciiCoded = []
for i in ascii:
if 65 <= i <= 90 or 97 <= i <= 122: # only letters get changed
asciiCoded.append(i+shiftValue)
else:
asciiCoded.append(i)
But you know what, python can do the whole of that in a single line, using list comprehension. Watch this:
Phrase = input('Type message to encrypt: ')
shiftValue = int(input('Enter shift value: '))
# encoding to cypher, in single line
stringCoded = ''.join(chr(ord(c)+shiftValue) if c.isalpha() else c for c in Phrase)
print(stringCoded)
A little explanation: the list comprehension boils down to this for loop, which is easier to comprehend. Caught something? :)
temp_list = []
for c in Phrase:
if c.isalpha():
# shift if the c is alphabet
temp_list.append(chr(ord(c)+shiftValue))
else:
# no shift if c is no alphabet
temp_list.append(c)
# join the list to form a string
stringCoded = ''.join(temp_list)
Much easier it is to use the maketrans method from the string module:
>>import string
>>
>>caesar = string.maketrans('ABCD', 'CDEF')
>>
>>s = 'CAD BA'
>>
>>print s
>>print s.translate(caesar)
CAD BA
ECF DC
EDIT: This was for Python 2.7
With 3.5 just do
caesar = str.maketrans('ABCD', 'CDEF')
And an easy function to return a mapping.
>>> def encrypt(shift):
... alphabet = string.ascii_uppercase
... move = (len(alphabet) + shift) % len(alphabet)
... map_to = alphabet[move:] + alphabet[:move]
... return str.maketrans(alphabet, map_to)
>>> "ABC".translate(encrypt(4))
'EFG'
This function uses modulo addition to construct the encrypted caesar string.
asciiCoded = []
final_ascii = ""
for i in ascii:
final_ascii = i+shiftValue #add shiftValue to ascii value of character
if final_ascii in range(65,91) or final_ascii in range(97,123): #Condition to skip the special characters
asciiCoded.append(final_ascii)
else:
asciiCoded.append(i)
print (asciiCoded)
Related
so I have the encrypt dictionary already set up:
alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ "
encrypt = "CDEFGHIJKLMNOPQRSTUVWXYZAB "
word = input("Enter the message: ")
d = {alphabet[i]: encrypt[i] for i in range(len(alphabet))}
and lets say I want to encrypt word, I would use replace()here, but if I use that one, it just replaces everything with A and B.
My code currently looks like that:
alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ "
encrypt = "CDEFGHIJKLMNOPQRSTUVWXYZAB "
d = {alphabet[i]: encrypt[i] for i in range(len(alphabet))}
word = input("Enter a word: ")
for key in d.keys():
word = word.upper().replace(key, d[key])
print(word)
and in the Terminal it prints out "BAAB".
Not sure why its only using the A and B and nothing more.
You can do it with .join():
alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ "
encrypt = "CDEFGHIJKLMNOPQ9RSTUVWXYZAB "
d = {a: e for a, e in zip(alphabet, encrypt)}
''.join(d[i] for i in 'word'.upper())
Output:
'XQSF'
You're replacing the letter one by one so the letter a is changed to c but in the next loop, c will be changed to e etc, you only have b and a because it's the 2 last letters in your code and the gap in your code is 2.
You have to use another variable and iterate through the initial variable and add the value letter in another variable from the key in the first variable
Not sure if it's really clear so here's an example :
word2 = ""
for i in word:
word2 += d[i]
First you replace every A with C, so you don't have As anymore (only B to Z). Then you replace every B with D, so you don't have Bs anymore, either (only C to Z). Etc. Near the end, you only have Ys and Zs left, and you replace them with A and B, respectively. That's why your output only has As and Bs.
You should make all replacements "in parallel" instead of sequentially like you do, and a proper way to do that is to use str.translate:
alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ "
encrypt = "CDEFGHIJKLMNOPQRSTUVWXYZAB "
trans = str.maketrans(alphabet, encrypt)
word = 'Chaos'
print(word.upper().translate(trans))
Output:
EJCQU
Something like this should solve it for you:
alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ "
encrypt = "CDEFGHIJKLMNOPQRSTUVWXYZAB "
d = {alphabet[i]: encrypt[i] for i in range(len(alphabet))}
print(''.join([d[i.upper()] for i in input("Enter a word: ")]))
Adding to #Nin17's answer and assuming the dictionary d is given, you can use map instead of a list comprehension to obtain the string:
res = "".join(map(d.get, "word".upper()))
As NicoCaldo points out, you may also simply add 2 to the ASCII numerical representation of each character, wrapping around and making an exception for the space character .
from_letter = ord("A")
to_letter = ord("Z")
range = to_letter - from_letter
offset = ord("B") - from_letter
res = "".join(map(lambda c: c if c == " " else chr(from_letter + ((ord(c)-from_letter+offset) % range)), "some words".upper()))
disclaimer im new to python
i need to split a string input send if to a function that substitutes a character in the string with a different character (like a substitution cipher) but i just dont know how to go about this
print('Welcome to the encryption protocol for top secret governemt cover ups')
string=input('whats your message?')
def encrypt(string):
alpha = "abcdefghijklmnopqrstuvwyz"
sub_alpha = "pokmenliuytrwqazxcvsdfgbhn"
index=0
while index < len(string):
letter=string[index]
im not really sure what im doing im really bad at python, this has had me stumped for 3 days now ive reviewed my course material and tried videos on youtube im probably just really really dumb
I think the key piece of knowledge you're missing is that strings are iterable. So you can do things like:
for c in "FOO":
print(c)
# prints "F\nO\nO\n"
And you can find the index of a character within a string with str.index. So you can build up your cyphertext like this:
alpha = "abcdefghijklmnopqrstuvwyz "
cypher = "pokmenliuytrw qazxcvsdfgbhn"
plaintext = "some string"
cyphertext = ""
for c in plaintext:
char_index = alpha.index(c)
cyphertext += cypher[char_index]
You can also iterate over things inline - this is called a comprehension. So to transform your string you can do this instead of using the for loop:
cyphertext = "".join(cypher[alpha.index(c)] for c in plaintext)
The example above uses the str.join function to concatenate each character of cyphertext.
Here is a solution that asks the question and then iterates through each letter, finding the index in the alpha key, and replacing it with the sub_alpha key equivalent.
Note this example also checks if it should be lowercase or uppercase.
EDIT: if the input character does not have a valid cipher, it doesn't get altered.
EDIT 2: expanded answer to convert both forwards and backwards.
alpha = "abcdefghijklmnopqrstuvwyz"
sub_alpha = "pokmenliuytrwqazxcvsdfgbhn"
def encrypt(in_char):
is_lower_case = in_char.islower()
index = alpha.find(in_char.lower())
if index < 0:
return in_char
elif is_lower_case:
return sub_alpha[index]
else:
return sub_alpha[index].upper()
def decrypt(in_char):
is_lower_case = in_char.islower()
index = sub_alpha.find(in_char.lower())
if index < 0:
return in_char
elif is_lower_case:
return alpha[index]
else:
return alpha[index].upper()
print('Welcome to the encryption protocol for top secret governemt cover ups')
input_str=input('whats your message? ')
output_str=""
for letter in input_str:
output_str += encrypt(letter)
print("Encrypted: ")
print(output_str)
input_str=""
for letter in output_str:
input_str+= decrypt(letter)
print("Decrypted: ")
print(input_str)
The first two functions are mine:
def rotated(n: int):
'''Returns a rotated letter if parameter is greater than 26'''
ALPHABET = 'abcdefghijklmnopqrstuvwxyz'
if n >= 26:
n %= 26
return ALPHABET[n:26] + ALPHABET[:n]
assert rotated(0) == 'abcdefghijklmnopqrstuvwxyz'
assert rotated(26) == 'abcdefghijklmnopqrstuvwxyz'
def Caesar_decrypt(text: str, key: int) -> str:
'''Returns a decryption of parameter text and key'''
text = text.lower()
key_to_zero = str.maketrans(rotated(key),rotated(0))
return text.translate(key_to_zero)
But my partner worked on the 3rd function:
def Caesar_break(code: str)-> str:
'Decrypts the coded text without a key'
file = open('wordlist.txt', 'r')
dic = []
dlist = file.readlines()
wl = []
l = []
cl = []
swl = []
sw = ''
for words in code:
if words.isalnum() or words.isspace():
l.append(words)
else:
l.append(' ')
Ncode = ''.join(l)
codelist = Ncode.split()
high = 0
for i in range(1,27):
highesthit = 0
hit = 0
out = Caesar_decrypt(Ncode, i)
e = 0
l = 0
while l < len(dlist):
dic.append(dlist[l].split()[0])
l += 1
while e < len(dic):
if out == dic[e]:
hit += 1
e += 1
if hit > highesthit:
high = i
highesthit = hit
return(Caesar_decrypt(Ncode, high))
I can't contact him right now, so I was wondering if there is a simpler way to break the Caesar code using brute force. My partner used too many random letters in his code, so I can't really understand it.
Note: "wordlist.txt" is a document we downloaded down with all of the words in the dictionary. Here is the link for reference.
The Caesar_break code is supposed to work like this:
Caesar_break('amknsrcp qagclac') == 'computer science'
code breaking! Yay! The simplest way to break the caeser cipher is to assume that your encoded text is representative of the actual language it's in with respect to the frequency of letters. In English, that relative frequency looks kind of like:
"etaoinshrdlcumwfgypbvkjxqz"
# most to least common characters in English according to
# https://en.wikipedia.org/wiki/Letter_frequency
The fastest way to break a Caeser Cipher, then, is to create a collections.Counter of the letters in your encrypted phrase, find the most common couple, and assume each one (in turn) is e. Calculate your difference from there, and apply the decrypt cipher. Test to see if it's valid English, and ta-da!
import collections
def difference(a: str, b: str) -> int:
a, b = a.lower(), b.lower()
return ord(b) - ord(a)
def english_test(wordlist: "sequence of valid english words",
text: str) -> bool:
"""english_test checks that every word in `text` is in `wordlist`"""
return all(word in wordlist for word in text)
def bruteforce_caeser(text: str) -> str:
with open('path/to/wordlist.txt') as words:
wordlist = {word.strip() for word in words}
# set comprehension!
c = collections.Counter(filter(lambda ch: not ch.isspace(), text))
most_common = c.most_common() # ordered by most -> least common
for ch, _ in most_common:
diff = difference('e', ch)
plaintext = Caeser_decrypt(text, diff)
if english_test(wordlist, plaintext):
return plaintext
There's a subtle logic error in this code though, w.r.t. an assumption made about the input text. I'll leave it as an exercise to the student to find the logic error and think of what small change could be made to ensure a result on any input. As a hint: try rotating and then decrypting the following phrase:
Judy I don't think it's right for you to contact such a marksman as this man, for without warning this marksman could shoot and kill you.
If you are sure that a ciphertext was encrypted with ceaser (x+3)mod25 you can just float letters. I would make all text lowercase first. then get asci values all chracters. For example asci(a)=97, make it 97-97=0; for b make it 98-97=1.Then I would make 2 arrays 1 for characters, 1 for integer values of chracters....
My question is how to improve the code so that it can adapt to however long the input message is. As is, the message must be 5 letters. I would like to improve the code such that a message of any length can be inputted and the cipher will work with it. Help would be much appreciated. :-) See the code below!
#Enter your message
message=raw_input('Enter your message here. Make sure to use all CAPS througout your message and leave no spaces in between words.')
length=len(message)
print 'The length of your message is ',length
#This statement is a possible idea to let the program know how many letters it will be need to shift. But I don't know how to actually do this.
print chr(length+64)
#Indexes letters out of message.
A=message[length-length]
B=message[length-length+1]
C=message[length-length+2]
D=message[length-length+3]
E=message[length-length+4]
#Shifts letters and accounts for shifting XYZ to ABC.
def shift(x):
if ord(x)+3==91:
return 65
if ord(x)+3==92:
return 66
if ord(x)+3==93:
return 67
else:
return ord(x)+3
a2=shift(A)
b2=shift(B)
c2=shift(C)
d2=shift(D)
e2=shift(E)
#Converts shifted ordinals back to characters
def convert(x):
return chr(x)
first=convert(a2)
second=convert(b2)
third=convert(c2)
fourth=convert(d2)
fifth=convert(e2)
#Prints resultant characters
print first,second,third,fourth,fifth
import string
shift_amt = 13
alphabet_lc = string.ascii_lowercase
shifted_lc = alphabet_lc[shift_amt:]+alphabet_lc[:shift_amt]
alphabet_uc = alphabet_lc.upper()
shifted_uc = shifted_lc.upper()
trans_tab = string.maketrans(alphabet_lc+alphabet_uc,shifted_lc+shifted_uc)
message = "Encode Me To a new MessaGez!"
print message.translate(trans_tab)
is one way of doing it in Python2 at least
Use two for loops, one for looping through each character, and one for shifting the character the desired amount of times. We use a function upper() to shift a character.
def upper(char):
from string import ascii_letters as _all
if char == ' ':
return ' '
return _all[_all.index(char)+1] if char != 'Z' else 'a'
def shift(message, key):
temp = []
for i in message:
char = i
for k in range(key):
char = upper(char)
temp.append(char)
return ''.join(temp)
message=raw_input('Enter your message here: ')
key = int(raw_input('Enter the desired key: '))
length=len(message)
print 'The length of your message is', length
print 'Your encrypted message is {0}'.format(shift(message, key))
This runs as:
bash-3.2$ python caesar.py
Enter your message here: This works WITH or without CAPS
Enter the desired key: 10
The length of your message is 31
Your encrypted message is drsC GyBuC gSdR yB GsDryED MKZc
bash-3.2$
The Ceasar cipher is built in in python 2;
In [6]: 'The Caesar cipher is built-in.'.encode('rot13')
Out[6]: 'Gur Pnrfne pvcure vf ohvyg-va.'
As you can see, this encoding only acts on letters, and it works for upper and lower case.
But is you want to remove spaces and make every thing upper-case, Python can do that as well;
In [9]: 'this is a string'.translate(None, ' \t')
Out[9]: 'thisisastring'
In [10]: 'this is a string'.translate(None, ' \t').upper()
Out[10]: 'THISISASTRING'
In [11]: 'this is a string'.translate(None, ' \t').upper().encode('rot13')
Out[11]: 'GUVFVFNFGEVAT'
Or in a different way;
In [15]: ''.join('this is a string'.split()).upper().encode('rot13')
Out[15]: 'GUVFVFNFGEVAT'
Bibliography:
Kid Snippets: "Math Class" (Imagined by Kids) - YouTube http://youtu.be/KdxEAt91D7k
Mary Had A Little Lamb Nursery Rhyme With Lyrics - YouTube http://youtu.be/CkRdvGmcCBE
Mary Had a Little Lamb - Wikipedia, the free encyclopedia http://goo.gl/FNEuyd
Python source code:
Note: working for negative shift numbers also
Note: if reverse shift then we do encode - decode message
Note: preserving spaces also
small_chars = [chr(item) for item in range(ord('a'), ord('z')+1)]
upper_chars = [item.upper() for item in small_chars]
def encode_chr(chr_item, is_upper_case):
'''
Cipher each chr_item.
'''
# setting orig and end order.
if is_upper_case:
orig_ord = ord('A')
end_ord = ord('Z')
else:
orig_ord = ord('a')
end_ord = ord('z')
# calculating shift
temp_ord = ord(chr_item)+shift
# calculating offset order with modulo.
# char is after end_ord, calculating offset
num_of_chars = 26
offset_ord = (temp_ord - end_ord - 1)%num_of_chars
return chr(orig_ord + offset_ord)
# enable while loop to repeat until status not 'y'
status = 'y'
while status == 'y':
# enter word to cipher.
word = raw_input("Word: ")
# enter char shift
shift = input("Shift: ")
print
# create cipher list variable
cipher = list()
# loop trough each char in word
for chr_item in word:
# encode just letters.
# replace non-alfa with underscore: "_"
if chr_item in upper_chars or chr_item in small_chars:
# set is_uppser_case to True for upper case chars.
is_upper_case = (chr_item in upper_chars) and True
# cipher char.
temp_chr = encode_chr(chr_item, is_upper_case)
# append ciphered char to list
cipher.append(temp_chr)
elif chr_item is ' ':
cipher.append(chr_item)
else:
cipher.append('_')
# print word
print word
# print ciphered word
print ''.join(cipher)
# repeat again for another word?
status = raw_input("Repeat? [y|n]: ")
print
Test cases:
>>>
Word: aAzZ!#
Shift: 1
aAzZ!#
bBaA__
Repeat? [y|n]: y
Word: aAzZ#!
Shift: -1
aAzZ#!
zZyY__
Repeat? [y|n]: y
Word: aAzZ#$
Shift: 27
aAzZ#$
bBaA__
Repeat? [y|n]: y
Word: aAzZ%^
Shift: -27
aAzZ%^
zZyY__
Repeat? [y|n]: n
>>>
Output:
Note: if reverse shift then we do encode - decode message
>>>
Word: "Mary Had a Little Lamb"
Shift: 1
"Mary Had a Little Lamb"
_Nbsz Ibe b Mjuumf Mbnc_
Repeat? [y|n]: y
Word: _Nbsz Ibe b Mjuumf Mbnc_
Shift: -1
_Nbsz Ibe b Mjuumf Mbnc_
_Mary Had a Little Lamb_
Repeat? [y|n]: n
>>>
Here is a simple Caesar cipher program written for Python 3 that should not be very difficult to rewrite for Python 2:
import string
def main():
key = 5
table = str.maketrans(string.ascii_letters,
string.ascii_lowercase[key:] +
string.ascii_lowercase[:key] +
string.ascii_uppercase[key:] +
string.ascii_uppercase[:key])
plaintext = input('Please enter a phrase: ')
ciphertext = plaintext.translate(table)
print('Your encrypted phrase is:', ciphertext)
if __name__ == '__main__':
main()
It's difficult to tell what is being asked here. This question is ambiguous, vague, incomplete, overly broad, or rhetorical and cannot be reasonably answered in its current form. For help clarifying this question so that it can be reopened, visit the help center.
Closed 10 years ago.
So, I need to input a scrambled alphabet, and then input a secret message using that alphabet. My program needs to unscramble the secret message. I have written this so far and am stuck. I was hoping someone could help me. What I tried so far is changing every index in the list to A-Z. My question is how do I use what I wrote for that and make it work for the secret message I input as well.
s = ()
alphabet = input("Please input the scrambled alphabet in order: ")
message = input("Now input the scrambled message: ")
alphabet.upper()
s = dict(alphabet)
num = 1
while num < 2:
s[0] = chr(65)
s[1] = chr(66)
s[2] = chr(67)
s[3] = chr(68)
s[4] = chr(69)
s[5] = chr(70)
s[6] = chr(71)
s[7] = chr(72)
s[8] = chr(73)
s[9] = chr(74)
s[10] = chr(75)
s[11] = chr(76)
s[12] = chr(77)
s[13] = chr(78)
s[14] = chr(79)
s[15] = chr(80)
s[16] = chr(81)
s[17] = chr(82)
s[18] = chr(83)
s[19] = chr(84)
s[20] = chr(85)
s[21] = chr(86)
s[22] = chr(87)
s[23] = chr(88)
s[24] = chr(89)
s[25] = chr(90)
num +=1
print (s)
for alpha in message.upper():
if alpha < "A" or alpha > "Z":
print(alpha,end="")
else:
print(s [ord(alpha) -65 ], end="")
First, creating the big dictionary s is entirely unnecessary. It literally does nothing: the line:
s [ord(alpha) -65 ]
Is turning a letter into a number, subtracting 65, and then putting it through a dictionary that adds 65 and turns it back into a number.
Secondly, the line alphabet.upper() doesn't actually change the alphabet, it just returns an uppercase version. You need to do
alphabet = alphabet.upper()
Now for the meaty part. What you meant to do was create a dictionary mapping letters in the code alphabet to letters in the real alphabet. The line dict(alphabet) doesn't do this. Instead, iterate through the characters in the string and assign each to the corresponding letter. You're on the right track using chr, but are going to way too much work. How about this:
s = {}
for i in range(26): # iterate from 0 to 25
s[alphabet[i]] = chr(65 + i)
since chr(65 + i) is the letter in the normal, ordered alphabet, and alphabet[i] is the scrambled one.
Once you have that dictionary, running through letter by letter and changing it should be easy (and it's left to you).
You could use the string translate() method:
import string
intab = 'abcdefghijklmnopqrstuvwxyz' # or string.ascii_lowercase
outtab = 'xyzabcdefghijklmnopqrstuvw'
tab = string.maketrans(intab, outtab)
s = raw_input('Type some text: ').lower()
print s.translate(tab)
maketrans() creates a table with both alphabets, and translate() just replace each character of the string by its pair in the table.
I don't know Python - this is pseudocode
create array of scrambled alphabet
create array of "normal" alphabet
foreach char in message, find char in scrambled array, then output corresponding "normal" char
You could make a dictionary:
alphabet({'a':'alpha', 'b':'bravo','c':'charlie', etc.})
so abc would translate to alphabravocharlie
where 'a','b',.. are the coded letters and 'alpha','bravo',... are their actual values. Make "cypher" by reversing this.
See the section of online tutorials on dictionaries:
http://docs.python.org/dev/library/collections.html
I believe you're asking 'If I type in a string of characters (I'm assuming a string with no delimiter), how can I make that a dictionary where the first letter = A, the second = B, etc.' (sorry if this is the incorrect interpretation). If that is the case, you could do something like this (note the usage of raw_input - input is a built-in that evaluates the parameter - it will not assign):
alphabet = raw_input("Please input the scrambled alphabet in order: ")
message = raw_input("Now input the scrambled message: ")
secret_map = {}
# Step through the provided string, incrementing the character number by index.
# Index will start at 65 + 0 = A (change to 98 if you want lowercase)
for index, letter in enumerate(alphabet):
secret_map[letter] = chr(65 + index)
# Now join the string together, mapping each letter to its corresponding value
new_str = ''.join([secret_map[char] for char in message])
# Print the resulting string
print new_str
Since this is homework (and perhaps you haven't gotten to list comprehensions), this is equivalent to creating new_str:
new_str = ''
for letter in message:
new_str += secret_map[letter]
Let me know if this isn't what you were looking for.
I just couldn't resist expanding on stummjr's answer to create a more pythonic version of both encoding and decoding (I added some extra variables to try and make each step more clear):
import random, string
cypher_list = list(string.ascii_lowercase)
random.shuffle(cypher_list)
cypher_text = ''.join(cypher_list)
encode_tab = string.maketrans(string.ascii_lowercase, cypher_text)
decode_tab = string.maketrans(cypher_text, string.ascii_lowercase)
orig_text = 'this means war'
crypt_text = orig_text.translate(encode_tab)
clear_text = crypt_text.translate(decode_tab)
print cypher_text
print crypt_text
print clear_text
assert(clear_text == orig_text)