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):
Related
I have been working on this codewars assignment the entire day, but I keep failing the test on code wars. Running into index errors and exit code (1) even though I am passing every test and attempt, the exit code seems so illusive yet confusing. I understand if my program doesn't do what it is supposed to but it is passing the tests, and right now there are some errors I don't quite understand messing up my program in ways that I can't even comprehend. Because they are returning the strings exactly as requested by the assignment, so why... is it not working? What have I done wrong? Please help me almighty code wizards, I am a noob...
Working on Split Strings: Complete the solution so that it splits the string into pairs of two characters. If the string contains an odd number of characters then it should replace the missing second character of the final pair with an underscore ('_').
Here is my Code:
def solution(string):
join_string = "-".join(string[i:i+2] for i in range(0, len(string), 2))
x_string = join_string.split("-")
underscore = "_"
index = (len(string) - len(x_string))
if index % 2 == 0:
for i in range(0, index):
return x_string
break
else:
x_string[index] = x_string[index] + underscore
return x_string
elif index > 2:
x_string[index] = x_string[index] + underscore
return x_string
elif index < 1:
x_string = []
return x_string
The error comes up at
x_string[index] = x_string[index] + underscore
IndexError: list index out of range
This line
join_string = "-".join(string[i:i+2] for i in range(0, len(string), 2))
already does the entire job you want, except for the underscore at the end if there were an odd number of characters in the input. Your code is making very heavy weather of this special case. Check if the input string has an odd number of characters:
if len(string) % 2 == 1:
and if it has, append an underscore to the returned value:
join_string += "_"
Your question doesn't say what the expected output is, and it's hard to tell from the code, so, to keep to the essential point, the whole function becomes
def solution(string):
join_string = "-".join(string[i:i+2] for i in range(0, len(string), 2))
if len(string) % 2 == 1:
join_string += "_"
return join_string
I get the output
>>> solution("123465")
'12-34-65'
>>> solution("1234657")
'12-34-65-7_'
>>> solution("1")
'1_'
>>> solution("")
''
If instead you want, say, a list of 2-strings, then do
return join_string.split("-")
I am new to python and I was trying to write a program that gives the frequency of each letters in a string from a specific point. Now my code is giving correct output for some inputs like if I give the input "hheelloo", I get the correct output, but if the input is "hheelllloooo", the frequency of h,e and l is printed correct, but the frequency of 'o' comes out as 7 if the starting point is index 0. Can somebody tell me what am i doing wrong.
Write a Python program that counts the occurrence of Character in a String (Do
not use built in count function.
Modify the above program so that it starts counting
from Specified Location.
str = list(map(str, input("Enter the string : ")))
count = 1
c = int(input("Enter the location from which the count needs to start : "))
for i in range(c, len(str)):
for j in range(i+1,len(str)):
if str[i] == str[j]:
count += 1
str[j] = 0
if str[i] != 0:
print(str[i], " appears ", count, " times")
count = 1
str = list(map(str, input("Enter the string : ")))
count = 1
c = int(input("Enter the location from which the count needs to start : "))
for i in range(c, len(str)):
for j in range(i+1,len(str)):
if str[i] == str[j]:
count += 1
str[j] = 0
if str[i] != 0:
print(str[i], " appears ", count, " times")
count = 1 // <----- This line should be outside the if block.
The error was because of indentation.
I have just properly indented the last line.
Thanks, if it works, kindly upvote.
string module can be useful and easy to calculate the frequency of letters, numbers and special characters in a string.
import string
a='aahdhdhhhh2236665111...//// '
for i in string.printable:
z=0
for j in a:
if i==j:
z+=1
if z!=0:
print(f'{i} occurs in the string a {z} times')
If you want to calculate the frequency of characters from a particular index value, you just have to modify the above code a little as follows:
import string
a='aahdhdhhhh2236665111...//// '
c = int(input("Enter the location from which the count needs to start : "))
for i in string.printable:
z=0
for j in a[c:]:
if i==j:
z+=1
if z!=0:
print(f'{i} occurs in the string a {z} times')
I don't think you need to map the input to list of str as input() always returns a string and string itself is a list of characters. Also make sure you don't use the built-ins as your variable names (As str used in your code). One of the simpler approach can be:
input_word = input("Enter the string : ")
c = int(input("Enter the location from which the count needs to start : "))
# Dict to maintain the count of letters
counts = {}
for i in range(c, len(input_word)):
# Increment 1 to the letter count
counts[input_word[i]] = counts.get(input_word[i], 0)+1
for letter, freq in counts.items():
print (f'{letter} appears {freq} times')
Output:
Enter the string : hheelllloooo
Enter the location from which the count needs to start : 2
e appears 2 times
l appears 4 times
o appears 4 times
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 question already has answers here:
Repeat string to certain length
(15 answers)
Closed 5 months ago.
In school we are currently using python to create a Caeser Cipher, and a Keyword Cipher. I need help with certain parts of the Keyword cipher, mainly repeating a word to match the length of a string that has been entered, For example:
Entered String: Hello I am Jacob Key: bye Printed text: byebyebyebyeb
I'm okay at Python, but this is very hard for me. Currently this is as far as I got:
def repeat_to_length(key, Input):
return (key * ((ord(Input)/len(key))+1))[:ord(Input)]
Since It's a string I thought if I used ord It would turn it to a number, but I realised when using the ord command you can only have a single character, as I realised when I repeatedly got this error:
TypeError: ord() expected a character, but string of length 16 found
I found some code that did a keyword cipher, but I am not sure which part does the process I am trying to code:
def createVigenereSquare():
start = ord('a')
end = start + 26
index = start
sq = [''] * 256
for i in range(start, end):
row = [''] * 256
for j in range(start, end):
if index > (end - 1):
index = start
row[j] = chr(index)
index += 1
sq[i] = row
index = i + 1
return sq
def createKey(message, keyword):
n = 0
key = ""
for i in range(0, len(message)):
if n >= len(keyword):
n = 0
key += keyword[n]
n += 1
return key
def createCipherText(message, key):
vsquare = createVigenereSquare()
cipher = ""
for i in range(0, len(key)):
cipher += vsquare[ord(key[i])][ord(message[i])]
return cipher
message = str(input("Please input a message using lowercase letters: "))
keyword = str(input("Please input a single word with lowercase letters: "))
key = createKey(message, keyword)
ciphertext = createCipherText(message, key)
print ("Message: " + message)
print ("Keyword: " + keyword)
print ("Key: " + key)
print ("Ciphertext: " + ciphertext)
As I've said I'm only okay at Python, so I don't really understand all the code in the above code, and I really want to be able to write most of it myself.
This is my code so far:
def getMode():
while True:
print("Enter encrypt, e, decrypt or d")
mode = input('Do you want to encrypt the message or decrypt? ') #changes whether the code encrypts or decrypts message
if mode in 'encrypt e decrypt d ENCRYPT E DECRYPT D'.split(): #defines the various inputs that are valid
return mode
else:
print('Enter either "encrypt" or "e" or "decrypt" or "d".') #restarts this string of code if the input the user enters is invalid
Input = input('Enter your message: ')
key = input('Enter the one word key: ')
def repeat_to_length(key, Input):
return (key * ((ord(Input)/len(key))+1))[:ord(Input)]
encryptedKey = repeat_to_length(key, Input)
print(encryptedKey)
I know I've been pretty long winded but if anyone could provide any information on this topic, like explaining the code for the keyword cipher, or just answering my question, I would appreciate it!
AnimeDeamon
Another possibility is to repeat the key enough times to get a string at least as long as the message, and then just grab as many characters as you need from the beginning:
>>> message='Hello I am Jacob'
>>> key='bye'
>>> times=len(message)//len(key)+1
>>> print((times*key)[:len(message)])
byebyebyebyebyeb
We compute times by dividing the length of the message by the length of the string, but we have to add 1, because any remainder will be dropped. times*key is just key repeated times times. This may be longer than we want, so we just take the first len(message) characters.
A simple one-liner could be
''.join(key[i % len(key)] for i in range(len(message)))
What it does, inside out:
We iterate over the indices of the letters in the string message
For each index, we take the remainder after division with the key length (using the % operator) and get the corresponding letter from the key
A list of these letters is constructed (using list comprehension) and joined together to form a string (the join method of an empty string)
Example:
>>> message = "Hello I am Jacob"
>>> key = "bye"
>>> print ''.join(key[i % len(key)] for i in range(len(message)))
byebyebyebyebyeb
I am trying to write my own python code to brute force a caesar cipher and I need some help. I specifically need help near the end of my code of the function. I want to know how to print specifically so that after each key tried there is a gap. I am using python 3.3 and have just started coding 3 weeks ago.
print ("The plaintext will be stripped of any formatting such as spaces.")
freqlist = []
maxkey = 26
if key > maxkey:
raise Exception("Enter a key between 0 and 26: ")
elif key < 0:
raise Exception("Enter a key between 0 and 26: ")
freq = []
inpt=input("Please enter the cipher text:")
inpt = inpt.upper()
inpt = inpt.replace(" ", "")
inpt = inpt.replace(",", "")
inpt = inpt.replace(".", "")
for i in range(0,27):
key = i
def decrypt():
for i in range(0,27):
for a in inpt:
b=ord(a)
b-= i
if b > ord("Z"):
b -= 26
elif b < ord("A"):
b+=26
freqlist.append(b)
for a in freqlist:
d=chr(a)
freq.append(d)
freq="".join(freq)
print(freq.lower(),"\n")
decrypt()
I am trying to use a for loop and I don't think it is really working effectively.
Based on the error you posted, I think this should help.
In Python, you can have local and global variables of the same name. The freq in the function is local, and thus the initialization of the global freq doesn't touch the local one. To use the global freq, you have to tell the function to do so, via the global statement. This is explained a little more in the Python FAQs.
That should be enough to get you back on track.
EDIT:
Below is an edit of your decrypt function, with some explanations of the changes
def decrypt():
# we don't need the zero start value, that's the default
# test all possible shifts
for i in range(27):
# initialize the array
freqlist = []
# shift all the letters in the input
for a in inpt:
b = ord(a)
b -= i
if b > ord("Z"):
b -= 26
elif b < ord("A"):
b+=26
freqlist.append(b)
# now put the shifted letters back together
shifted = ""
for a in freqlist:
d = chr(a)
# append the shifted letter onto our output
shifted += d
# after we put the decrypted string back together, print it
# note this is outside the letter loops,
# but still inside the possible shifts loop
# thus printing all possible shifts for the given message
print(d)