So I'm in the middle of making a simple Caesar cipher for practice and I can't get it to decipher entire strings, just individual letters.
symbol_add is the function in question.
Here's the code:
import re
alphabet = "abcdefghijklmnopqrstuvwxyz"
def cleanIt(clean):
global alphabet
s = re.sub('[^a-z]+', '?', str(clean))
return s
def symbol_add(symbol, key):
encryptedMsg = ""
for x in symbol:
position = alphabet.find(x)
newPosition = (position + key) % 26
newLetter = alphabet[nyPosisjon]
encryptedMsg += nyBokstav
return encryptedMsg
def cipher(data,key):
text = ""
if data in alphabet:
text += symbol_add(symbol=data,key=key)
return text
def main():
try:
msg = (input("Write the message you would like to encrypt\n"))
key = int(input("which key would you like to use?\n"))
cleanIt(clean=msg)
print(cipher(data=msg, key=key))
except ValueError:
print("Write a number!")
main()
I'm sure the solution is pretty simple, still learning.
Any help in how to solve this will be greatly appreciated!
import re
alphabet = "abcdefghijklmnopqrstuvwxyz"
def cleanIt(clean):
global alphabet
s = re.sub('[^a-z]+', '?', str(clean))
return s
def symbol_add(symbol, key):
position = alphabet.find(symbol)
newPosition = (position + key) % 26
newLetter = alphabet[newPosition]
return newLetter
def cipher(data,key):
text = ""
for letter in data:
if letter in alphabet:
text += symbol_add(symbol=letter,key=key)
return text
def main():
try:
msg = (input("Write the message you would like to encrypt\n"))
key = int(input("which key would you like to use?\n"))
# Note: you need to assign msg to be equal to cleanIt(clean=msg).
# Just calling cleanIt(clean=msg) won't work, as strings
# are immutable in Python
msg = cleanIt(clean=msg)
print(cipher(data=msg, key=key))
except ValueError:
print("Write a number!")
main()
The main changes are:
The for loop was moved from symbol_add to cipher, so that symbol_add gets called for each character
In main(): cleanIt(clean=msg) -> msg = cleanIt(clean=msg); the reason for this is that strings are immutable in Python, meaning that you need to reassign the variable msg to essentially point to the new string.
Output of this code:
Write the message you would like to encrypt
test
which key would you like to use?
1
uftu
Also, try to stick to a single naming convention; you have a function that follows camelCase (cleanIt) and another that follows snake_case (symbol_add). Try and name all functions in the same way. (The convention in Python is to use snake_case for functions)
You can streamline your cipher method quite a bit if you fill a dictionary as lookup when entering the method. It contains the mapping based on the key provided and maps your input characters to to the cipher-character.
Its much faster to look up in a dict then to .index() into a string.
Using dict.get(key[,default]) allows to provide the '?' for unknowns so you need no import re and no preprocessing.
Read about dict.get(): Why dict.get(key) instead of dict[key]?
Adding uppercase mapping to the chiffre is trivial as well based on the lower case ones:
alphabet = "abcdefghijklmnopqrstuvwxyz"
def cipher(data, key):
# in case you change alphabet
la = len(alphabet)
# get the default lookup
chiffre = { c:alphabet[(i+key)%la] for i,c in enumerate(alphabet) }
# create lookup for upper cases as well
chiffre.update( { c.upper():n.upper() for c,n in chiffre.items() } )
# supply ? for all unknowns, use the knowns where possible and return as string
return ''.join( (chiffre.get(c,"?") for c in data) )
def main():
try:
msg = (input("Write the message you would like to encrypt\n"))
key = int(input("which key would you like to use?\n"))
print(cipher(data=msg, key=key))
except ValueError:
print("Write a number!")
main()
Output:
Write the message you would like to encrypt
Hello World
which key would you like to use?
1
Ifmmp?Xpsme
Related
For the get_letter_from_user function, while using the while loop for validation, it keeps repeating the invalid input; I want to make sure that it is a single letter and lower case, and I want to make sure that it doesn't equal the second parameter of the function. I'm not sure what I'm doing wrong, though. (and how to get gud at coding if u have tips)
def get_text_from_user(prompt):
return input(prompt).lower()
def get_letter_from_user(prompt, not_allowed):
not_allowed = ''
allowed = input(prompt).lower()
while not allowed == not_allowed or allowed.isalpha() or len(allowed) > 1:
allowed = str(input('Invalid letter, try again:'))
return allowed
def main():
text = get_text_from_user("Enter some text: ")
ltr1 = get_letter_from_user("Enter a letter: ", '')
ltr2 = get_letter_from_user("Enter another letter: ", ltr1)
new_text = text.replace(ltr1,ltr2)
print("The new text is", new_text)
if __name__ == "__main__":
main()
Suggestion for the function:
def get_letter_from_user(prompt, not_allowed):
allowed = input(prompt).lower()
while allowed == not_allowed or len(allowed) > 1:
print('not_allowed:',not_allowed)
allowed = str(input('Invalid letter, try again:'))
return allowed
ltr1 = get_letter_from_user("Enter a letter: ", '')
ltr2 = get_letter_from_user("Enter another letter: ", ltr1)
Sample output:
Enter a letter: d
Enter another letter: d
not_allowed: d
Invalid letter, try again:d
not_allowed: d
Invalid letter, try again:a
To replace a letter or sequence of letters in a string, you might want to take a look at the string.replace() function:
text = input('Enter some text: ')
find = input('Enter a letter to replace: ')
replace_with = input(f'Enter a letter to replace \'{find}\' with: ')
replaced = text.replace(find, reolace_with)
print('The new text is:', replaced)
To add another little detail because you asked how to get better at coding:
I would never make a function with a parameter that is immediately changed to an empty string. Like:
def get_letter_from_user(prompt, not_allowed):
not_allowed = ''
Rather use a default value like this:
def get_letter_from_user(prompt, not_allowed=''):
...
Key = int(input('How much would you like to shift your code by?'))
message = input('Write the message that you would like to encrypt.')
def cipherText(message):
for letter in message:
intValue = ord(letter)
convInt = intValue + Key
print(chr(convInt))
print("Here is your ciphertext:")
S2 = str(cipherText(message))
def plainText():
for letter in S2:
intValue = ord(letter)
convInt = intValue - Key
print(chr(convInt))
print('The following is your decrypted message')
plainText()
What is wrong with this code? It prints out klkb. Why is that?
First you do this:
S2 = str(cipherText(message))
Then:
for letter in S2:
But since cipherText doesn't return anything, it returns the default None.
And since you force that result into a string, it is 'None' - that's what then gets mangled in your plainText() function.
This is a good example of why you should avoid type casting if you can. Also, you're using global variables to pass data around, where you could just pass in what you want as parameters to the function.
Finally, you don't follow Python naming conventions, which makes the code harder to read for someone familiar with regular Python. Key looks like a class (it should be key) and cipherText should be cipher_text. Some libraries deviate from this for documentation reasons across multiple platforms and languages, but when writing your own Python, you should try to follow normal naming conventions.
Your code with all those issues fixed:
a_key = int(input('How much would you like to shift your code by?'))
a_message = input('Write the message that you would like to encrypt.')
def cipher_text(message, key):
result = ''
for letter in message:
int_value = ord(letter)
conv_int = int_value + key
result += chr(conv_int)
return result
garbled_message = cipher_text(a_message, a_key)
print("Here is your cipher text: ", garbled_message)
def plain_text(garbled_message, key):
result = ''
for letter in garbled_message:
int_value = ord(letter)
conv_int = int_value - key
result += chr(conv_int )
return result
message2 = plain_text(garbled_message, a_key)
print('The following is your decrypted message: ', message2)
Of course, having two functions that do the same thing, with extra steps, is not ideal either. Here's a shorter version:
a_key = int(input('How much would you like to shift your code by?'))
a_message = input('Write the message that you would like to encrypt.')
def cipher_text(message, key, decrypt=False):
if decrypt:
key = -key
return ''.join([chr(ord(ch) + key) for ch in message])
garbled_message = cipher_text(a_message, a_key)
print(f'Here is your cipher text: {garbled_message}')
decrypted_message = cipher_text(garbled_message, a_key, decrypt=True)
print(f'The following is your decrypted message: ', decrypted_message)
Today I made a .py file that decrypts strings encrypted with a vigenere square. I have gotten this far but I cant seem to add spaces to the ciphr list and encr_txt because it garbles the decrypted message. Instead of "message is, hello my name is slim shady", you get "message is, hellprvmwhwebwrw k d thady", where as if i leave spaces out of encr_txt and the ciphr list I get a fine message. I do not know how to fix this there are no errors either, I just started coding in python a couple days ago so if its obvious i'm sorry. Also I know this could be done way easier but im learning lists so i chose to make it this way instead of something like this:
Another question i found relating my problem but does not describe my situation
Code :
# -*- coding: utf-8 -*-
# ^ encoding
# Encrypted text
# encr_txt = 'tkedobaxoudqrrffhhhalbmmcnedeo'
encr_txt = 'qexpg vy zeen ie wdrm elsmy'
#encr_list = list(encr_txt)
txtpos = 0
# Key to ^
key = 'james'
keypos = 0
limit = len(encr_txt)
limitpos = 0
# Vigenere square
ciphr = ['abcdefghijklmnopqrstuvwxyz ',
'bcdefghijklmnopqrstuvwxyz a',
'cdefghijklmnopqrstuvwxyz ab',
'defghijklmnopqrstuvwxyz abc',
'efghijklmnopqrstuvwxyz abcd',
'fghijklmnopqrstuvwxyz abcde',
'ghijklmnopqrstuvwxyz abcdef',
'hijklmnopqrstuvwxyz abcdefg',
'ijklmnopqrstuvwxyz abcdefgh',
'jklmnopqrstuvwxyz abcdefghi',
'klmnopqrstuvwxyz abcdefghij',
'lmnopqrstuvwxyz abcdefghijk',
'mnopqrstuvwxyz abcdefghijkl',
'nopqrstuvwxyz abcdefghijklm',
'opqrstuvwxyz abcdefghijklmn',
'pqrstuvwxyz abcdefghijklmno',
'qrstuvwxyz abcdefghijklmnop',
'rstuvwxyz abcdefghijklmnopq',
'stuvwxyz abcdefghijklmnopqr',
'tuvwxyz abcdefghijklmnopqrs',
'uvwxyz abcdefghijklmnopqrst',
'vwxyz abcdefghijklmnopqrstu',
'wxyz abcdefghijklmnopqrtsuv',
'xyz abcdefghijklmnopqrtsuvw',
'yz abcdefghijklmnopqrtsuvwx',
'z abcdefghijklmnopqrtsuvwxy',
'abcdefghijklmnopqrtsuvwxyz ']
first = ciphr[0]
string = ''
def start():
global limitpos
limitpos += 1
global keypos
for i in ciphr:
if keypos == len(key):
keypos = 0
else:
pass
if i[0] == key[keypos]:
#print "%s, %s" % (i[0], i)
global currenti
currenti = i
#print currenti
finder()
break
else:
pass
def finder():
global keypos
global txtpos
done = False
position = 0
while done == False:
for i in currenti[position]:
if i == '_':
pass
if i == encr_txt[txtpos]:
global string
string = string + first[position]
#print "message is, %s" % string
keypos += 1
txtpos += 1
done = True
if limitpos == limit:
print "message is, %s" % string
break
else:
start()
else:
position += 1
pass
start()
Adding spaces to the table changes the way the cipher works. You can't expect to make that kind of change and not affect the way messages are encrypted and decrypted!
As an aside, the last row of your table is incorrect. It's identical to the first row, but it should have the space in the first position.
I Am Trying To Make A Caesar Cipher In Python. The Code I Have Written Is Trying To Use An Array Then It Will Be Re-Arranged In A Iteration By The Amount Of The Key. I Am A Few Errors So Any Advice On How To Make The Array System Work Is Appreciated. Would This Idea Work Or Should I Give Up On This Method . Thanks
The Errors i Am Getting Are In The Key Subprogram With It Restarting If The Number Does Not =1-26
import sys
Alphbet =["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"]
def Menu():
print("Welcome To Password Protect Pc Optimizer 3000 Edition!")
print("\n")
print("1.) Encrypt A New Password And Save It")
print("2.) Access An Existing Saved Password ")
print("3.) Just A One Off Encryption ")
print("4.) Quit Password Protect ")
Answer= input("Please Enter An Option Number:")
if Answer=="1":
Key()
elif Answer==2:
Option2()
elif Answer==3:
Option3()
elif Answer==4:
Quit()
else:
Menu()
def Key():
global Key
Key = input("Please Set A Ceaser Cihper Key (1-26)")
Validation =1
if Key ==1:
Validation +=1
Option1()
Removed A Lot Of elif's Here
elif Key ==26:
Validation +=1
Option1()
if Validation ==1:
print("Please Enter A Valid Number")
Key()
def Option1():
Hold=["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","26"]
for i in range(Key):
Temp= Alphabet[Hold]
Alphabet[Hold]= Alphabet[Hold-1]
Alphabet[Hold-1] =Temp
print(Alphabet)
Menu()'
Here is a simplified version:
from collections import deque
import string
class CaesarCypher(object):
def __init__(self, cypher):
encrypt_rotate = deque(string.ascii_lowercase)
encrypt_rotate.rotate(-cypher)
decrypt_rotate = deque(string.ascii_lowercase)
decrypt_rotate.rotate(cypher)
encrypt_trans = ''.join(encrypt_rotate)
decrypt_trans = ''.join(decrypt_rotate)
self.encrypt_table = str.maketrans(string.ascii_lowercase, encrypt_trans)
self.decrypt_table = str.maketrans(string.ascii_lowercase, decrypt_trans)
def encrypt(self, text):
return text.translate(self.encrypt_table)
def decrypt(self, text):
return text.translate(self.decrypt_table)
First import deque to use its rotate method that will shift the elements position, then import string which contains the alphabet so we don't have to declare it.
Then create a class to encapsulate the encrypt and decrypt methods. Define a __init__ method so the class will take an argument for the cypher and prepare the translation tables that will shift the characters.
Finally add 2 methods to the class to encrypt and decrypt based on the translation table.
Now you can use your class this way:
caesar = CaesarCypher(5)
test = caesar.encrypt('Hello World!')
print(test)
# > 'Hjqqt Wtwqi!'
test2 = caesar.decrypt(test)
print(test2)
# > 'Hello World!'
Edit:
This example only takes care of lowercase letters, if you want to encrypt uppercase you need to declare a separate table for string.ascii_uppercase and translate the text twice on the decrypt and encrypt methods, first for lower then for upper.
Edit2: If you are on python 2.7 the maketrans method is on the string class not in str. Thanks to #t.m.adam for the comment.
I have a program that I'm working on that takes an input and checks it to see if it spelled correctly with a dictionary inside of a file. However, I want to return a suggestion or two as well of what the person means. Any suggestions of how to do this? I have found some modules that can do it, but not for a specific dictionary from a file. Any help is appreciated!!
Here is what I have now:
def getDictionary():
theDictionary = open("theDictionary.txt", "r")
dictionaryList = []
for eachLine in theDictionary:
splitLines = eachLine.split()
dictionaryList.append(splitLines[0])
theDictionary.close()
return dictionaryList
def spellChecker(theFile, theDictionary):
lowerItems = theFile.lower()
wordList = lowerItems.split()
wrongList = []
for item in wordList:
if item not in theDictionary:
result = False
wrongList.append(item)
else:
result = True
wrongItem = ""
return (result, wrongList)
def main():
theDictionary = getDictionary()
theText = getFile()
theInput = input("Input some words here:")
result, wrongList=spellChecker(theInput,theDictionary)
if result:
print("There are no spelling errors in the sentence! Hooray!")
else:
if len(wrongList) == 1:
print('There is a spelling error in the sentence! The word that is wrong is "' + str(wrongList) + '".')
elif len(wrongList) > 1:
print('There are some spelling errors in the sentence! The words that are wrong are"' + str(wrongList) + '".')
main()
You might want to have a look at the difflib module in the Standard Library. It allows you to do approximate string matching, which seems to be what you want.
It really does not matter if your dictionary is inside a file or not, since you are loading it into a list anyway. Maybe have a look at the get_close_matches() method in the said module.