Ceaser Cipher-Python, using large numbers for shifts - python

I need help with my ceaser cipher code. I need to be able to shift/decrypt a letter even when the shift value is greater than 122. My code only works for shift values that are less than 22. The code fails when the user specifies an input for the shifter that is greater than 122 How can I do this? For a with shifter 123 the output should be r
k = int(raw_input("Please enter a value for k: ")) #Shifter number
original = raw_input("plaintext: ") #Message user wants ciphered
original_as_array = list(original) ##I am turning the input into an array
for i in range(0,len(original)): ##Now seperating each character to add k
char = original_as_array[i]
charint = ord(char)
if charint >= 65 and charint <=90:
cipher_int = ((charint-65 + k) % 26)+65
code_char = chr(cipher_int)
print code_char,
elif charint >= 97 and charint <=122:
cipher_int = ((charint-97 + k) % 26)+97
code_char = chr(cipher_int)
print code_char,
else:
print char,

#Caesar Cipher
#Function that reads and writes data to the file, and calls the encryption and decryption methods.
#It isolates the key and choice of the user, and then calls the appropriate function.
def isolate():
#Open input file in read mode
fin = open('Input3.txt', 'r')
#Open output file in write mode
fout = open('Out1.txt', 'w')
#Create a list that holds each line of the file as an element of the list
container = fin.readlines()
#For loop that goes through each line of the file (contianed in list) and isolates the choice and key of the user
#Then it removes the key and choice from the split input list and calls the appropriate method.
for i in container:
# List of words, with whitespace and trailing '\n' removed
words = i.split()
# Interpret and remove the last two words
key = int(words[len(words) - 1])
choice = words[len(words) - 2]
words.remove(str(key))
words.remove(choice)
# Re-join the words of the message. The message may differ from the original if there is consecutive whitespace.
message = ' '.join(words)
message = message.upper()
# Encrypt or decrypt to fout
if choice == 'e':
fout.write(encrypt(message, key) + '\n')
else:
fout.write(decrypt(message, key) + '\n')
#Close the file to make sure data is written properly to the file.
fout.close()
#Encryption method, which takes two parameters, the users message and key, and returns the newly encrypted message.
def encrypt(message, key):
#Empty string that will contain the encrypted message.
encrypted_message = ""
#Alphabet string, which contains all the letters of the alphabet at their index locations.
alpha = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
#For loop that goes through each character of the users message and, if a space, adds it straight to the encrypted message or encrypts it through modular division.
for i in message:
#If the character is a space, add it to the final message.
if i == ' ':
#Concatenate the space to the final message
encrypted_message += ' '
#If the character is not a space, determine the final locatin out of index range, % by 26 and re-add the character at this index to the encrypted_message.
else:
#Determine the final location of the character (out of index range)
location = key + alpha.index(i)
#% this location by 26 to determine the location within the proper index.
location %= 26
#Finally, concatenate the letter at this location to the final message.
encrypted_message += alpha[location]
#When the function is called, have it return the final encrypted message.
return encrypted_message
#Decryption method that takes two parameters, the users message and key
def decrypt(message, key):
#Create a string that reverses the alphabet, since we are moving backwards when we decrypt.
reverse_alpha = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'[::-1]
decrypted_message = ""
#For loop that runs through each element of the users message and, if space, adds it to the message, or encrypts using % division, then concatenates to final message.
for i in message:
if i == ' ':
decrypted_message += ' '
else:
#Determine the final location out of index range of the encrypted character
location = key + reverse_alpha.index(i)
#% this position by 26 to determine the index location in range of the letter.
location %= 26
#Finally, add the letter at this location to the decrypted message.
decrypted_message += reverse_alpha[location]
#Converts the decrypted message into lowercase form.
decrypted_message = decrypted_message.lower()
return decrypted_message
#Call the function isolate5(), which initiates the program.
isolate()

Your code fails because you didn't specify a condition when ord(char) > 122. Instead, you threw it to the else clause.
You can just cut the part <= 122:
for k in range(20):
for i in range(0,len(original)): ##Now seperating each character to add k
char = original[i]
charint = ord(char)
if charint >= 65 and charint <=90:
cipher_int = ((charint-65 + k) % 26)+65
code_char = chr(cipher_int)
print code_char,
elif charint >= 97:
cipher_int = ((charint-97 + k) % 26)+97
code_char = chr(cipher_int)
print code_char,
else:
print char,
With k set to 18, this will produce r. If this solution wasn't what you meant, feel free to coment below!

Related

Decrypting Caeser Cipher in Python

I am trying to make a decrypt script for a Caeser cipher script to encrypt but can't get it to work. This is my encryption script:
def encrypt(text,s):
result = ""
# traverse text
for i in range(len(text)):
char = text[i]
# Encrypt uppercase characters
if (char.isupper()):
result += chr((ord(char) + s-65) % 26 + 65)
# Encrypt lowercase characters
else:
result += chr((ord(char) + s - 97) % 26 + 97)
return result
#def decrypt(ciphertext, s):
text = "0123456789"
s = 4
Cipher=encrypt(text,s)
print("Text : " + text)
print ("Shift : " + str(s))
print ("Cipher: " + encrypt(text,s))
I need help with creating a decrypt script the same way.
Thanks in advance!
Here is some code of mine for a function used to decrypt a ceaser cipher. The approach used when the shift is not known is simply to get every possible value, then pick the one with more then half the decoded words being in the English dictionary. For non alphabetic characters, there is a placeholder character used.
There are some extra parts required, such as the dictionary or extra functions / lists so I will include them in the below links.
A note, it does only work for on lowercase letters however, I do hope it will help you to understand the logic a bit better.
def Decode(message, shift=-1):
"""
Decodes a message from a caesar cipher
Params:
- message (str) : Message to decode
Optional Params:
- shift (int) : If shift is known
Returns:
- decodedMessage (str) : The decoded message
"""
decodedMessage = ''
message = message.lower() # lowercase is easier to work with
# If the shift is known it is simple to decode
if shift != -1:
for letterIndex in range(len(message)):
if message[letterIndex] in [' ', '!', '?', '.', '-', ',', '_']:
decodedMessage += message[letterIndex]
else:
try:
index = ALPHABET.index(message[letterIndex]) - shift
# If the index is smaller then ALPHABET, handle it
while index < 0:
index += len(ALPHABET)
decodedMessage += ALPHABET[index]
except Exception as e:
print("A problem occured:", e)
decodedMessage += '?'
return decodedMessage
else: #If shift is not known, figure it out thru brute force
data = read_json('words_dictionary')
for i in range(len(ALPHABET)):
decodedMessage = Decode(message, i+1)
wordList = decodedMessage.split(" ")
try:
# Loop over words counting english words
count = 0
for word in wordList:
if word in data.keys():
count += 1
# More accurate this way compared to only one word checks
if count > len(wordList) / 2:
return decodedMessage
except KeyError:
continue
Full code here
Word dictionary here

How do you split duplicate letters with "x" in Python?

I am trying to create a function which is able to detect when two letters back to back are duplicates, for example the ls in "hello", and split the duplicate letters with the letter "x". Here is my code:
plaintext = input("Enter plaintext here: ")
plaintext = plaintext.lower() # makes plaintext lowercase
plaintext = plaintext.replace(" ", "") # removes all spaces
# this separates all duplicate letters
i = 0 # sets i to 0
for letter in plaintext:
if plaintext[-1] == plaintext[-2]: # if the last letter is the same as the second to last
plaintext = plaintext[:-1] + "x" + plaintext[-1:] # separate them with an x
elif plaintext[i] == plaintext [i+1]: # if one letter is the same as the next letter
# the line above makes an error
plaintext = plaintext[:i+1] + "x" + plaintext[i+1:] #separate them with an x
i += 1
else:
i += 1
This code works when I enter hello there as the input; I receive helxlothere. However, when I test another input, such as heythere, IndexError: string index out of range shows up for elif line (line 12). How can I make this code work for all inputs?
You can use regex to achieve this.
For both approaches, it will work for hello there hey there
The difference comes when more than two character repetition happens.
approach 1
import re
string='hello there'
# find Any repeated character and add x in between them
answer = re.sub(r'(.)\1{1}', r'\1x\1', string)
print(answer)
Here for hellllo there text, you will get output helxllxlo there
approach 2
alternatively, you can use this method.
s="hello there"
for match in re.finditer(r"(.)\1+", s):
old=s[match.start():match.end()]
s=s.replace(old,'x'.join(old))
print(s)
here for hellllo there text, you will get output helxlxlxlo there as output.
I think the second approach will be more appropriate.
The IndexError is caused by the fact that you are looking at plaintext[i+1]. As you can see in the word heythere, there are no letters which match back to back, and therefore the code continues until it hits the end, and so you get an IndexError because there is no element i+1.
You can fix this by using this code instead:
plaintext = input("Enter plaintext here: ")
plaintext = plaintext.lower() # makes plaintext lowercase
plaintext = plaintext.replace(" ", "") # removes all spaces
# this separates all duplicate letters
i = 0 # sets i to 0
for letter in plaintext:
if plaintext[-1] == plaintext[-2]: # if the last letter is the same as the second to last
plaintext = plaintext[:-1] + "x" + plaintext[-1:] # separate them with an x
try:
elif plaintext[i] == plaintext [i+1]: # if one letter is the same as the next letter
# the line above makes an error
plaintext = plaintext[:i+1] + "x" + plaintext[i+1:] #separate them with an x
i += 1
else:
i += 1
except IndexError:
pass
This code should stop your code from crashing in the elif statement, while also completing properly.
Hope this helps, have a nice day!
You are receiving the IndexError because during iterating, when the loop reaches the last letter,
elif plaintext[i] == plaintext [i+1]:
this line checks for the letter after the last letter, which does not exist, which causes the program to run into IndexError.
You have to check till the second last letter, for it to work properly.
A simple and easier way to arrive at the same output with a bit easier logic.
Logic
Create a new string and insert all letters which are in the old string, plaintext, to the new string, newPlainText, and check only for one condition that is whether the last letter is same as current or not and if yes then also insert letter 'x' into the newPlainText and that's it!
plaintext = input("Enter plaintext here: ")
plaintext = plaintext.lower() # makes plaintext lowercase
plaintext = plaintext.replace(" ", "") # removes all spaces
# this separates all duplicate letters
newPlainText = plaintext[0] # copy the first letter of plaintext to the new string which will handle the duplicates and insert `x` between them
i = 1 # sets i to 1
while i < len(plaintext):
if plaintext[i] == plaintext[i-1]: # we check that if prev and current letters are same then also append x to the new string
newPlainText += "x"
newPlainText += plaintext[i] # we insert the required current letter to the new string in each iteration
i += 1
print(newPlainText)

Ignoring spaces in Vigenere Cipher

I've coded an encryption and decryption program for vigenere cipher but I'm stuck when it comes to involve spaces. What condition could I use to overcome it and ignore it completely
print("Enter the string to be encrypted")
s=input()
print("Enter the key for encryption")
t=input()
r=0
s=list(s)
t=list(t)
key=[]
encrypted=[]
decrypted=[]
#This is the loop for making the key string
for i in range(0,len(s),len(t)):
r=i
for j in range(0,len(t),1):
if(i<len(s)):
if(len(key)<len(s)):
key.append(t[j])
i=i+1
i=r
print("The encrypted key is: ")
key=''.join(key)
print(key)
#This is the code for encrypting the message with the key string
for i in range(0,len(s)):
x = (ord(s[i]) + ord(key[i])) % 26
x += ord('A')
encrypted.append(chr(x))
print("The encrypted string is: ")
encrypted=''.join(encrypted)
print(encrypted)
#For decryption
for i in range(0,len(s)):
x = (ord(encrypted[i]) - ord(key[i])+26) % 26
x += ord('A')
decrypted.append(chr(x))
print("The decrypted string is: ")
decrypted=''.join(decrypted)
print(decrypted)
Here Key string refers to as ->
STRING INPUT - WATERMELON
Key - LEMON
Key string - LEMONLEMON
But it isn't working for sentences. I tried putting in code like
if(s[i]==' '):
continue
but it didn't work
Possible solution, hope it's not too python2
from itertools import cycle
# if python2 uncomment the following
# from itertools import izip as zip
# you can replace these with calls for input()
data = "attack at dawn"
key = "lemon"
# sanitize input
data = "".join(data.upper().split()) # trick to remove all white-space chars
key = key.upper()
print data
print key
ord_A = ord('A')
# encripting
encrypted = []
# looping together the input and the key
# cycle gives an infinite loop over an iterable
for dc, kc in zip(data, cycle(key.upper())):
ec = ord_A + (ord(dc) + ord(kc)) % 26
encrypted.append(chr(ec))
encrypted = "".join(encrypted)
print(encrypted)
# decrypting
decrypted = []
for ec, kc in zip(encrypted, cycle(key.upper())):
dc = ord_A + (ord(ec) - ord(kc)) % 26
decrypted.append(chr(dc))
decrypted = "".join(decrypted)
print(decrypted)```

How do i undo a cypher code for any words entered?

I have the following caesar code function: I want to be able to undo the cyphering and return the original word, what i have here works for the word "hello" it cyphers the word as "xvdei", and decyphers it as "hello". However it will not work for any other words i put, i want to know how i can go about to edit this out so that any word i put will decypher back into the original word.So like if i put xvdei i will get back "hello" or if i put in hijklm and shift it by 7 i will get back "aaaaaa"
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']
def decypher(text,shift):
# initialize ciphertext as blank string
dtext = ""
# loop through the length of the plaintext
for i in range(len(text)):
# get the ith letter from the plaintext
l = text[i]
# find the number position of the ith letter
n_alphabet = alphabet.index(l)
# find the number position of the cipher by adding the shift
c_num = (n_alphabet + shift ) % len(alphabet) - 6 - i
# find the cipher letter for the cipher number you computed
c_letter = alphabet[c_num]
# add the cipher letter to the ciphertext
dtext = dtext + c_letter
# return the computed ciphertext
return dtext
I need to decypher what this function gives me to the original word:
def caesar(plaintext,shift):
# initialize ciphertext as blank string
ciphertext = ""
# loop through the length of the plaintext
for i in range(len(plaintext)):
# get the ith letter from the plaintext
letter = plaintext[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
cipher_num = (num_in_alphabet + shift + i) % len(alphabet)
# find the cipher letter for the cipher number you computed
cipher_letter = alphabet[cipher_num]
# add the cipher letter to the ciphertext
ciphertext = ciphertext + cipher_letter
# return the computed ciphertext
return ciphertext
Reversing the cypher only needs to continue shifting up to the point where it reaches a complete cycle (i.e. by the rest of the letters):
def decypher(text,shift): return cypher(text,len(alphabet)-shift)
also, in your code:
cipher_num = (num_in_alphabet + shift + i) % len(alphabet)
should be
cipher_num = (num_in_alphabet + shift) % len(alphabet)

Repeating a word to match the length of a string [duplicate]

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

Categories

Resources