Work out the difference between letters python - python

been given an assignment and it's nearly finished. Just struggling with the last bit. The program is given a caesar cipher text, it then works out what the most frequent letter is and prints this back to the terminal. (Where I am up to.)
It will then suggest a key shift based on the most frequent letter and the user can then manually input this key shift, or their own key shift and the text is the deciphered.
I need the program to take the most frequent letter in the caesar text and compare this to the letter 'E' which is the most frequent letter in the english language and then work out how many key shifts it is away...
e.g. if the most common caesar text letter is n then n-e = 9.
Code so far:
import sys
def decrypt(plain, key):
"returns a Caesar cipher text given plain text and a key"
cipher = ""
for index in range(len(plain)):
if plain[index].isalpha():
if plain[index].islower():
cipher = cipher + chr((ord(plain[index]) -101- key+26) % 26+ 101)
else:
cipher = cipher + chr((ord(plain[index]) -65- key+26) % 26+ 65)
else:
cipher = cipher + plain[index]
return cipher #do nothing here
#main program
key = int(sys.argv[4])
action = sys.argv[2]
try:
in_file = open(sys.argv[1], "r")
except:
sys.exit("There was an error opening the file: {}".format(sys.argv[1]))
try:
out_file = open(sys.argv[3], "w")
except:
sys.exit("There was an error opening the file: {}".format(sys.argv[3]))
line = in_file.readline()
freq_dict = { }#letter : 0 for letter in LETTERS }
while len(line) != 0:
for letter in line.replace(" ",""):
if letter in freq_dict:
freq_dict[letter] += 1
else:
freq_dict[letter] = 1
line = in_file.readline()
cipher = decrypt(line, key)
out_file.write(cipher)
in_file.close()
out_file.close()
for letter in freq_dict:
print(letter, "times", freq_dict[letter])
Thanks in advance.

So it seems your decrypt function essentially generates an output that is the input text string shifted by the key right now.
From what I understand, what you then want to do is find the most frequently occurring letter in this string.
You can use the collections module to do this
import collections
most_freq = collections.Counter(cipher).most_common(1)[0]
Now all you are left with is to find the shift between your most_freq letter and e.
Perhaps the simplest way is just to enumerate the alphabet in a list and then find the index differences between the two.
alphabet = ['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']
shift = alphabet.index(most_freq) - alphabet.index('e')
Remember this shift gets you from e to your most_freq letter so when you apply the shift to your text you need to apply the opposite ( -1 * shift ) to get the right result.
Hope this helps.

Related

Use a condition within a loop

A simple substitution cipher may be created by shifting or rotating the alphabet by a certain number of places. Using this system with a rotation of 5 gives us the following alphabets:
Plaintext alphabet: ABCDEFGHIJKLMNOPQRSTUVWXYZ
Ciphertext alphabet: FGHIJKLMNOPQRSTUVWXYZABCDE
Any plain text message can be translated into the ciphertext by replacing each letter of the plain text alphabet with the letter in the ciphertext alphabet in the equivalent position. Spaces are left unchanged. For example, using the cipher above, the word DOG is enciphered as OBK.
Given a String and a rotation value, return the String translated into the ciphertext using the simple substitution method described above. You may assume that the text contains only spaces or capital letters and that the rotation value is always non-negative
function name: rotate_text
arguments:
text - input text to be encoded
n - an integer value specifying how many characters to rotate the text by
returns: a string containing the text rotated as described above
Testing
I am able to pass the test, but the result said I am u therenable to pass hidden or more test, Could someone help me?
def rotate_text(string1, int1):
loL = ['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']
strtoList = list(string1)
list1 = []
newStr = ""
if int1 == len(loL):
int1 = int1 % 26
for i in range(len(strtoList)):
if strtoList[i] in loL:
loLindex = loL.index(strtoList[i])
list1 += [loL[loLindex + int1]]
elif strtoList[i] == " ":
list1 += [" "]
for i in range(len(list1)):
newStr += list1[i]
return newStr
You need:
list1 += [loL[(loLindex + int1) % len(loL)]]
for whenever the cypher "loops back to the first letters".
And then
if int1 == len(loL):
int1 = int1 % 26
becomes irrelevant as well.
And BTW, you don't need to build a list and then make it a string. You can grow your string directly too...

Python Vigenere Cipher with required functions

I am working on an online course that has us creating a caesar cipher and vigenere cipher, but we first created two functions; one to find the position of a letter in an alphabet variable, and one to rotate one given character a given amount of times (I have seen that ord() and chr() work better, but the assignment wants us to focus on simpler concepts for now, I guess).
I was able to get the caesar function working, but am unsure as how to go forward with the vigenere cipher. I have watched many videos and looked around this site, but have not found any that allow for the preservation of spaces and non alphabetical characters. Can anyone point me in the right direction of how to start the vigenere function?
#Create function alphabet_position(letter) to turn letter into number
#such as a=0 or e=4, using lowercase to make sure case doesnt matter.
def alphabet_position(letter):
alphabet ="abcdefghijklmnopqrstuvwxyz" #Lists alphabet for a key
lower_letter = letter.lower() #Makes any input lowercase.
return alphabet.index(lower_letter) #Returns the position of input as a number.
def rotate_character(char, rot):
alphabet = "abcdefghijklmnopqrstuvwxyz"
if char.isalpha():
a = alphabet_position(char)
a = (a + rot) % 26 #needs modulo
a = (alphabet[a])
if char.isupper():
a = a.title()
return a
else:
return char
def encrypt(text, rot):
list1 = ""
for char in text:
list1 += rotate_character(char, rot)
return list1
def main():
x = input("Type a message: ")
y = input("Rotate by: ")
#result = rotate_character(x, y) #Not needed once encrypt function works.
result = encrypt(x, y)
print (result)
if __name__ == '__main__':
main()
following my comments; using all printables as alphabet:
from string import ascii_letters, digits, punctuation, whitespace
ALPHABET = ascii_letters + digits
STATIC_ALPHABET = punctuation + whitespace
# minor speedup
ALPHA_INDEX = {a: i for i, a in enumerate(ALPHABET)}
STATIC_ALPHABET_SET = set(STATIC_ALPHABET)
MOD = len(ALPHABET)
def encrypt(char, key):
if char in STATIC_ALPHABET_SET:
return char
else:
return ALPHABET[(ALPHA_INDEX[char] + key) % MOD]
def decrypt(char, key):
if char in STATIC_ALPHABET_SET:
return char
else:
return ALPHABET[(ALPHA_INDEX[char] + MOD - key) % MOD]
key = 17
plain = 'Hello World!'
enc = ''.join(encrypt(char, key) for char in plain)
print(enc) # YvCCF dFICu!
dec = ''.join(decrypt(char, key) for char in enc)
print(dec) # Hello World!

Nested loops slowing down program. How can I make it faster?

import re
file=input("What is the name of your file? ")
def words_from_file(filename):
try:
f = open(filename, "r")
words = re.split(r"[,.;:?\s]+", f.read())
f.close()
return [word for word in words if word]
except IOError:
print("Error opening %s for reading. Quitting" % (filename))
exit()
dictionary_file=words_from_file("big_word_list.txt")
newfile=words_from_file(file)
def dictionary_check(scores, dictionary_file, full_text):
count=0
for item in full_text:
if item in dictionary_file:
count+=1
scores.append(count)
def decoder(item,shiftval):
decoded = ""
for c in item:
c=c.upper()
if c in "ABCDEFGHIJKLMNOPQRSTUVWXYZ":
num = ord(c)
num += shiftval
if num > ord("Z"):
num=num-26
elif num < ord("A"):
num=num+26
decoded+=chr(num)
else:
decoded = decoded + c
return decoded
shiftval=0
scores=[]
while shiftval<=26:
full_text=[]
for item in newfile:
result=decoder(item,shiftval)
full_text.append(result)
shiftval+=1
print(full_text)
dictionary_check(scores, dictionary_file, full_text)
highest_so_far=0
for i in range(len(scores)):
if scores[i]>highest_so_far:
i=highest_so_far
i+=1
else:
i+=1
fully_decoded=""
for item in newfile:
test=decoder(item,highest_so_far)
fully_decoded+=test
print(fully_decoded)
Hey everybody.
I have this assignment where I had to make a program that decodes a shift cipher. Right now it works, but it's incredibly slow. I suspect it's probably because of the nested loops. I'm not really sure where to go from this point.
Some explanation of the code: The program reads in an encrypted file where each letter is shifted by a certain amount (i.e With a shift of 5, every A would now be an F. This would be done for every letter). The program reads in a dictionary file as well. There are only 26 possible shifts so for each shift it will decode the file. The program will take the file for each possible shift and compare it to the dictionary file. The one that has the most in common with the dictionary file will be reprinted as the final decrypted file.
Thank you everybody!
https://drive.google.com/file/d/0B3bXyam-ubR2U2Z6dU1Ed3oxN1k/view?usp=sharing
^ There is a link to the program, dictionary, encrypted and decrypted files.
Just change line 16 :
dictionary_file=set(words_from_file("big_word_list.txt"))
So the if item in dictionary_file: is executed in constant time instead of linear time. The program runs now in 4 seconds, disabling print statements,
and changing i=highest_so_far in highest_so_far=i, and capitalizing dictionary.

Using caesar cipher to decrypt ciphertext

I am attempting to decrypt the ciphertext "htrgti" to plaintext using this code. I keep getting "wigvix" which is not the message I should be getting.
Given the plain text (which should be a common word or phrase) and key, for every spot that said ciphertext I replaced it with plaintext, and the same for every spot that says plaintext:
def caesar(ciphertext, shift):
alphabet=["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"]
plaintext = ""
for i in range(len(ciphertext)):
letter = ciphertext[i]
# Find the number position of the ith letter
num_in_alphabet = alphabet.index(letter)
# Find the number position of the cipher by adding the shift
plain_num = (num_in_alphabet + shift) % len(alphabet)
# Find the plain letter for the cipher number you computed
plain_letter = alphabet[plain_num]
# Add the cipher letter to the plaintext
plaintext = plaintext + plain_letter
return plaintext
Your function works if you call it correctly, as hinted by #Mr.Llama, with the right shift caesar("htrgti", 11) is secret. Your function itself runs correctly. Here is a better version of your code, but it still has the problem that each .index lookup is O(alphalen). So, as a bonus, I added the implementation of the suggestion by #Two-Bit Alchemist to use str.translate.
import string
alphabet = string.ascii_lowercase
alphalen = len(alphabet)
shift = 11
def caesar(ciphertext, shift):
plaintext = ""
for letter in ciphertext:
num_in_alphabet = alphabet.index(letter)
plain_num = (num_in_alphabet + shift) % alphalen
plain_letter = alphabet[plain_num]
plaintext = plaintext + plain_letter
return plaintext
print(caesar("htrgti", shift))
# secret
table = str.maketrans(alphabet, alphabet[shift:] + alphabet[:shift])
print("htrgti".translate(table))
# secret

how to find alphabetic value of a keyword

I am currently trying to produce a program that allows me to encrypt and decrypt a words using a keyword, I have been told to use alphabetic value to do add from another words alphabetic value I know that I need to use ord or chr but I am not very confident in using this as I'm a beginner at programming, however I have no idea how to do this and would much appreciate, if someone could explain this to me with some examples.
s = "foo!#bar"
enc_key = "key"
# get sum of ord's of letters in key modulo 256
sm_ords= sum(map(ord,enc_key)) % 256
# add sum of ord's in encryption letter in key to ord of each char in s and use chr to create a new char
enc = "".join(chr((sm_ords + ord(ch))) for ch in s)
# reverse the encryption using - ord
dec = "".join(chr((ord(ch) - sm_ords)) for ch in enc)
print(enc)
print(dec)
¯¸¸jl«ª»
foo!#bar
This code encrypts / decrypts a string by adding the value of each of the keyword letters to each character in the string to be encrypted.
def encryptfunction():
result = ""
addedup = 0
for letter in wordtoencrypt:
for letter2 in keyword:
addedup = addedup + ord(letter2)
result = result + chr(ord(letter) + addedup)
return result
def decryptfunction():
result = ""
addedup = 0
for letter in wordtoencrypt:
for letter2 in keyword:
addedup = addedup + ord(letter2)
result = result + chr(ord(letter) - addedup)
return result
wordtoencrypt = input("Enter the word to encrypt:")
keyword = input("Enter the keyword:")
encrypt = int(input("encrypt(1) or decrypt(0)"))
if encrypt == 1:
print(encryptfunction())
else:
print(decryptfunction())

Categories