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)
This was made to brute force caesar ciphers using a dictionary file from http://www.math.sjsu.edu/~foster/dictionary.txt. It is run through three functions, lang_lib() which makes the text of the dictionary into a callable object, isEnglish(), which checks the percentage of the phrase, and if at least 60% of it matchwa with the any words in the dictionary, it would return a True value. Using this, a caeser cipher function runs through all shifts, and checking them from english words. It should return the result with the highest percentage, but it only seems to work through shifts 1-18. I can't figure out why it isn't working.
def lang_lib():
file = open('dictionary.txt', 'r')
file_read = file.read()
file_split = file_read.split()
words = []
for word in file_split:
words.append(word)
file.close()
return words
dictionary = lang_lib()
def isEnglish(text):
split_text = text.lower().split()
counter = 0
not_in_dict = []
for word in split_text:
if word in dictionary:
counter += 1
else:
not_in_dict.append(word)
length = len(split_text)
text_percent = ((counter / length) * 100)
#print(text_percent)
if text_percent >= 60.0:
return True
else:
return False
alphabet = "abcdefghijklmnopqrstuvwxyz0123456789!##$%/."
def caeser(text): #Put in text, and it will spit out all possible values
lower_text = text.lower()
ciphertext = "" #stores current cipher value
matches = [] #stores possible matches
for i in range(len(alphabet)): #loops for the length of input alphabet
for c in lower_text:
if c in alphabet:
num = alphabet.find(c)
newnum = num - i
if newnum >= len(alphabet):
newnum -= len(alphabet)
elif newnum < 0:
newnum += len(alphabet)
ciphertext = ciphertext + alphabet[newnum]
else:
ciphertext = ciphertext + c
testing = isEnglish(ciphertext)
for text in ciphertext:
if testing == True and len(ciphertext) == len(lower_text):
matches.append(ciphertext)
return i, matches
ciphertext = "" #clears ciphertext so it doesn't get cluttered
print(caeser('0x447 #0x$x 74w v0%5')) #shift of 19
print(caeser('zw336 #zw9w 63v uz#4')) #shift of 18
Thanks guys.
This part is indented too far as #tripleee suggested:
testing = isEnglish(ciphertext)
for text in ciphertext:
if testing == True:
matches.append(ciphertext)
return i, matches
Also you don't need to check the length if you have the indentation right and let the previous loop complete....
I found out that the dictionary.txt does not contain 2 or 3 letter words, so it would skew long inputs with many of these words, and return False. I added a list of common words, so now all inputs work accurately.
If anyone wants to help me make this code more efficient, I'd love some pointers. I am very new to Python.
I'm making this Caesar Cipher decoder and I want the program to print every single option (the 26 ways it could be shifted). However, when I run my code nothing shows, what was my error. If you know please tell me, I'm new to coding and in need of help.
import sys
import time
L2I = dict(zip("ABCDEFGHIJKLMNOPQRSTUVWXYZ",range(26)))
I2L = dict(zip(range(26),"ABCDEFGHIJKLMNOPQRSTUVWXYZ"))
msg = ("What is the intercepted message \n")
for character in msg:
sys.stdout.write(character)
sys.stdout.flush()
time.sleep(0.1)
msg_ans = input("> ")
msg_ans = msg_ans.strip()
shift = 0
def decipher(msg_ans,shift):
while shift < 26:
for i in msg_ans.upper():
if i.isalpha() == True :
msg_ans += I2L[ (L2I[i]+ shift)%26 ]
shift += 1
else:
msg_ans += i
shift += 1
print (msg_ans)
decipher(msg_ans,shift)
I expect it to output the 26 ways it can be shifted. However when I put the word 'Hello' I get 'HelloHFNOSMKSTXRQZBGWUCDHBAJLQLKTVAVVFIO' instead of 'IFMMP JGNNQ ...'
There are a couple of issues. First, you're incrementing shift every time you check a single character. In reality, you only want to increment it after each time you cycle completely through the message. You should also move the initialization into the function. There's no reason to pass shift in, since you're just trying all 26 possibilities in order.
def decipher(msg_ans):
shift = 0
while shift < 26:
for i in msg_ans.upper():
if i.isalpha() == True :
msg_ans += I2L[ (L2I[i]+ shift)%26 ]
else:
msg_ans += i
shift += 1
print (msg_ans)
At this point, though, there's no reason to use a while loop instead of a for:
def decipher(msg_ans):
for shift in range(26):
for i in msg_ans.upper():
if i.isalpha() == True :
msg_ans += I2L[ (L2I[i]+ shift)%26 ]
else:
msg_ans += i
print (msg_ans)
The other issue is that you're just appending the new characters to the end of your input string. You don't specify what form you actually want it in, so let's say you want it in a list of strings. You'll need to initialize the list, build a temporary string on each iteration, and then append the temporary string to the list:
def decipher(msg_ans):
possible_messages = []
for shift in range(26):
decoded_msg = ''
for i in msg_ans.upper():
if i.isalpha() == True :
decoded_msg += I2L[ (L2I[i]+ shift)%26 ]
else:
decoded_msg += i
possible_messages.append(decoded_msg)
return possible_messages
Then just print the result of your invocation of the function:
print(decipher(msg_ans))
msg should be like this
msg = "What is the intercepted message \n"
Also you probably want to print instead of return here
return msg_ans
Running python 3.5.
I'm start a study of basic encryption and I decided to try my hand at writing a simple Caesar cipher. Pretty straight forward logic:
1) for a given plaintext message, find the index for each message symbol in my LETTERS string
2) add the shift key to the index
3) the resulting number is the index of the cipher symbol
4) if the resulting number is greater than the length of my LETTERS string, then subtract the length of the LETTERS string from the number (this handles the wrap around back to the beginning of the string.
So here is the code for that program.
caesarCipher2.py
LETTERS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrustuvwxyz1234567890!##$%^&*()><.,?/"
message = str(input("Enter a message. "))
key = int(input("Enter a whole number key (1-79). "))
mode = str(input("Press 'E' to encrypt or 'D' to decrypt. "))
def encrypt_message(plain_message):
translated = " "
for symbol in plain_message:
if symbol in LETTERS:
num = LETTERS.find(symbol)
num += key
if num > len(LETTERS):
num -= len(LETTERS)
translated += LETTERS[num]
else:
translated += symbol
return translated
def decrypt_message(cipher_message):
translated = " "
for symbol in cipher_message:
if symbol in LETTERS:
num = LETTERS.find(symbol)
num -= key
if num < 0:
num += len(LETTERS)
translated += LETTERS[num]
else:
translated += symbol
return translated
def main():
if mode == "E" or mode == "e":
print(encrypt_message(message))
elif mode == "D" or mode == "d":
print(decrypt_message(message))
if __name__ == "__main__":
main()
Program seems to work ok, however as I'm running test cases, I start noticing that some shift keys are throwing an IndexError at the following line of the encrypt_Message():
translated += LETTERS[num]
So I decided to write another script, using the code from the encrypt_Message() to test any given message for all possible keys. What I found was that any plaintext message I pass through the function will result in a few of the shift keys (usually 5 - 10 keys) throwing an IndexError at that same line. All the rest of the keys return the ciphertext as intended.
Debugging the code on these error throwing keys shows me that some point in translating the plaintext message for these specific keys, the line:
num = LETTERS.find(symbol)
returns the length of LETTERS instead of the index of the symbol within LETTERS and then the code seems to hang up from there. The if statement doesn't fire off to adjust the num variable and so by the time it reaches the translated statement, the num variable is index out of bounds.
My question is why is this happening? Why is the code working as intended for the majority of the keys, while throwing this exception for the remaining?
Any thoughts?
Thanks.
Python indexes lists starting at 0. This will have the following effects:
>>> x = ['a', 'b', 'c', 'd']
>>> len(x)
4
>>> x[0]
'a'
>>> x[3]
'd'
>>> x[4]
IndexError: list index out of range
Notice that x[4] is already out-of-scope for a list with 4 elements. As a rule of thumb, the maximum index that can be considered inbounds is len(x) - 1.
In your case, the mistake is
if num > len(LETTERS):
which should be
if num >= len(LETTERS):
Eventually I will be able to post simple questions like this in a chat room, but for now I must post it. I am still struggling with comparison issues in Python. I have a list containing strings that I obtained from a file. I have a function which takes in the word list (previously created from a file) and some 'ciphertext'. I am trying to Brute Force crack the ciphertext using a Shift Cipher. My issue is the same as with comparing integers. Although I can see when trying to debug using print statements, that my ciphertext will be shifted to a word in the word list, it never evaluates to True. I am probably comparing two different variable types or a /n is probably throwing the comparison off. Sorry for all of the posts today, I am doing lots of practice problems today in preparation for an upcoming assignment.
def shift_encrypt(s, m):
shiftAmt = s % 26
msgAsNumList = string2nlist(m)
shiftedNumList = add_val_mod26(msgAsNumList, shiftAmt)
print 'Here is the shifted number list: ', shiftedNumList
# Take the shifted number list and convert it back to a string
numListtoMsg = nlist2string(shiftedNumList)
msgString = ''.join(numListtoMsg)
return msgString
def add_val_mod26(nlist, value):
newValue = value % 26
print 'Value to Add after mod 26: ', newValue
listLen = len(nlist)
index = 0
while index < listLen:
nlist[index] = (nlist[index] + newValue) % 26
index = index + 1
return nlist
def string2nlist(m):
characters = ['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']
numbers = [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25]
newList = []
msgLen = len(m) # var msgLen will be an integer of the length
index = 0 # iterate through message length in while loop
while index < msgLen:
letter = m[index] # iterate through message m
i = 0
while i < 26:
if letter == characters[i]:
newList.append(numbers[i])
i = i + 1
index = index + 1
return newList
def nlist2string(nlist):
characters = ['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']
numbers = [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25]
newList = []
nListLen = len(nlist)
index = 0
while index < nListLen:
num = nlist[index]
newNum = num % 26
i = 0
while i < 26:
num1 = newNum
num2 = numbers[i]
if (num1 == num2):
newList.append(characters[i])
i = i + 1
index = index + 1
return newList
def wordList(filename):
fileObject = open(filename, "r+")
wordsList = fileObject.readlines()
return wordsList
def shift_computePlaintext(wlist, c):
index = 0
while index < 26:
newCipher = shift_encrypt(index, c)
print 'The new cipher text is: ', newCipher
wordlistLen = len(wlist)
i = 0
while i < wordlistLen:
print wlist[i]
if newCipher == wlist[i]:
return newCipher
else:
print 'Word not found.'
i = i + 1
index = index + 1
print 'Take Ciphertext and Find Plaintext from Wordlist Function: \n'
list = wordList('test.txt')
print list
plainText = shift_computePlaintext(list, 'vium')
print 'The plaintext was found in the wordlist: ', plainText
When the shift amount = 18, the ciphertext = name which is a word in my wordlist, but it never evaluates to True. Thanks for any help in advance!!
It's hard to be sure with the information we have so far, but here's a guess:
wordsList = fileObject.readlines()
This is going to return you a list of strings with the newlines preserved, like:
['hello\n', 'my\n', 'name\n', 'is\n', 'jesi\n']
So, inside shift_computePlaintext, when you iterate over wlist looking for something that matches the decrypted 'vium', you're looking for a string that matches 'name', and none of them match, including 'name\n'.
In other words, exactly what you suspected.
There are a few ways to fix this, but the most obvious are to use wlist[i].strip() instead of wlist[i], or to strip everything in the first place by using something like wordsList = [line.strip() for line in fileObject] instead of wordsList = fileObject.readlines().
A few side notes:
There is almost never a good reason to call readlines(). That returns a list of lines that you can iterate over… but the file object itself was already an iterable of lines that you can iterate over. If you really need to make sure it's a list instead of some other kind of iterable, or make a separate copy for later, or whatever, just call list on it, as you would with any other iterable.
You should almost never write a loop like this:
index = 0
while index < 26:
# ...
index = index + 1
Instead, just do this:
for index in range(26):
It's easier to read, harder to get wrong (subtle off-by-one errors are responsible for half the frustrating debugging you will do in your lifetime), etc.
And if you're looping over the length of a collection, don't even do that. Instead of this:
wordlistLen = len(wlist)
i = 0
while i < wordlistLen:
# ...
word = wlist[i]
# ...
i = i + 1
… just do this:
for word in wlist:
… or, if you need both i and word (which you occasionally do):
for i, word in enumerate(wlist):
Meanwhile, if the only reason you're looping over a collection is to check each of its values, you don't even need that. Instead of this:
wordlistLen = len(wlist)
while i < wordlistLen:
print wlist[i]
if newCipher == wlist[i]:
return newCipher
else:
print 'Word not found.'
i = i + 1
… just do this:
if newCipher in wlist:
return newCipher
else:
print 'Word not found.'
Here, you've actually got one of those subtle bugs: you print 'Word not found' over and over, instead of only printing it once at the end if it wasn't found.