Python vigenere cipher going further than needed? - python

I am attempting to create the vigenere cipher in python and there seems to be a problem. Here is my encryption code:
def encryption():
plaintext=input("Please enter the message you wish to encode.")
#This allows the user to enter the message they wish to encrypt.
keyword=input("Please enter your keyword, preferably shorter than the plaintext.")
#This allows the user to enter a keyword.
encoded=""
#This creates a string for the user to input the encrypted message to.
while len(keyword)<len(plaintext):
#This begins a while loop based on the fact that the length of the keyword is shorter than the length of the plaintext.
keyword+=keyword
#This repeats the keyword.
if len(keyword)>len(plaintext):
#This sees if the string length of the keyword is now longer than the string length of the plaintext.
newkey=keyword[:len(plaintext)]
#This cuts the string length of the keyword
for c in range(len(plaintext)):
char=ord(plaintext[c])
temp=ord(keyword[c])
newchar=char+temp
if newchar>ord("Z"):
newchar-=26
newnewchar=chr(newchar)
encoded+=newnewchar
print(encoded)
I cannot seem to find the problem with it, however when I enter the plaintext "hello" and the keyword "hi" it come up with the following symbols: ¶´º»½. I think the addition in the for loop may be going too far.

You need to understand the ord() function, chr() is the inverse of ord()
for i in range(300):
print(str(i) + ' ' + chr(i))
If you don't use Unicode characters, you can use an alphabet string
alphabet = 'abcdefghijklmnopqrstuvwxyz'
for p,k in zip(plaintext,keyword): # you can directly iterate strings
char = alphabet.index(p)
temp = alphabet.index(k)
newchar = char + temp
if newchar > 25:
newchar -= 25
newchar = alphabet[newchar]

I won't just solve the bug for you as there are many other things to improve here !
Put space around operators: a=b should be a = b, same with +, -, etc.
I find it better to use function params than input. You can always have a second function to get input and encrypt the input:
def encryption(plaintext, keyword):
pass # (do nothing)
I'll let you write the auxiliary function.
I, and most, usually put comments on the line above the corresponding code. Also, no need to write This every time, and imperative is generally preferred.
Now let's have a look at your while loop. The condition is len(keyword) < len(plaintext), inside you check len(keyword) > len(plaintext). When can that happen ? Only during the last iteration. So move the code out of the loop.
Also, what you do inside the if doesn't need an if:
any_string[:len(any_string) + n] == any_string (n being a positive int).
Plus you never use newkey!
So we can simplify the loop to:
# Begin a while loop based on the fact that the length of the keyword is
# shorter than the length of the plaintext.
while len(keyword) < len(plaintext):
# Repeat the keyword.
keyword += keyword
# Cut the string length of the keyword
keyword = keyword[:len(plaintext)]
Which is equivalent to:
# Do the operation once
txt_len = len(plaintext)
# Get a string just a bit longer than plaintext
# Otherwise we would have issues when plaintext is not a multiple
# of keyword
keyword *= txt_len // len(keyword) + 1
# Chop of the extra part
keyword = keyword[:txt_len]
Note that both will fail when len(keyword) == 0.
Now to the for loop:
You could use zip, as polku showed you, but I'll assume it's too complicated for now and keep the range.
You could also use the alphabet, but plain arithmetic can do the trick:
alphabet.index(x) == ord(x) - ord('a'), so in your code:
char = ord(plaintext[c]) - ord('a')
temp = ord(keyword[c]) - ord('a')
newchar = char + temp
# Or simply:
newchar = ord(plaintext[c]) + ord(keyword[c]) - 194 # 2 * ord('a')
If we ignore capital letters, we can safely substitute
if newchar > 25:
newchar -= 25
# With
newchar %= 25
Finally:
alphabet[i] == ord(i + ord('a')).
Here's all this put together:
def encryption(plaintext, keyword):
# Do the operation once
txt_len = len(plaintext)
# Get a string just a bit longer than plaintext
# Otherwise we would have issues when plaintext is not a multiple
# of keyword
keyword *= txt_len // len(keyword) + 1 # // is floor division
# Chop of the extra characters (if there are any)
keyword = keyword[:txt_len]
# Create a string to store the encrypted message
encoded = ""
# Now you should change this to work with capital letters
for c in range(txt_len):
# 194 = 2 * ord('a')
newchar = ord(plaintext[c]) + ord(keyword[c]) - 194
newchar %= 25
encoded += chr(newchar + 97) # 97 = ord('a')
return encoded
def encrypt_input():
# This function should use input to get plaintext and keyword
# Then use encryption with those strings and print the result
pass

Indeed the addition goes too far, because ord uses ASCII (where A is 65), while de Vigenère had A in the first position. You could subtract ord('A'). The code also assumes all characters are capital letters. Here's a variation that uses a few of Python's library functions to perform the task.
import string, itertools
def encrypt(text, key='N'): # default to rot13 (:
'''Performs a Vigenere cipher of given plaintext and key'''
result=[]
key=key.upper()
for plain,shift in itertools.izip(text,itertools.cycle(key)):
shiftindex=string.ascii_uppercase.index(shift)
shiftto=(string.ascii_uppercase[shiftindex:] +
string.ascii_uppercase[:shiftindex])
trans=string.maketrans(string.ascii_letters,
shiftto.lower()+shiftto)
result.append(plain.translate(trans))
return ''.join(result)
A more complete variant might only consume key for letters, but this will be fine if your strings only contain letters. The reason I stuck to the ASCII letters is because locale specific alphabets might not have the intended ordering nor a matching set of upper and lower case (for instance, german ß). It's also entirely possible to translate the key into a list of translation tables only once.

Related

Issues iterating through strings and caesar shifting up and down at the same time

so I'm making a cipher and I want a program to easily encrypt and decrypt messages with this cipher, only issue is, I'm relatively new to Python and am not very good at this highly advanced stuff. I'm focusing on the encryption process right now. I've made a short guide on how to encrypt messages in this cipher in this document:
Cipher
I am having trouble finding a way to iterate through the individual characters in the string, shifting even characters down and odd ones up by the catalyst, and making the first character of each new word odd regardless of its placement in the string (and shifting numbers) all at the same time. I have the catalyst changing, reversing of the string, etc working just fine, it's just the actual encryption part that's giving me issues.
Here is my code so far:
# MTBE stands for Message To Be Encrypted
# Getting Catalyst
catalyst = int(input("Catalyst: "))
# The message to encrypt
mtbe = input("Message to encrypt: ")
# Reversing each word in the message
new_mtbe_one = reverseWordSentence(mtbe)
print(new_mtbe_one)
# Actual encrypting
catalyst_reset = 6
#
# Iteration/shifting stuff goes in here
#
# Code for updating catalyst (goes inside interation loop)
if catalyst < catalyst_reset:
catalyst += 1
elif catalyst >= catalyst_reset:
catalyst = 0
catalyst_reset += 1
(reverseWordSentence function you see in the code):
def reverseWordSentence(Sentence):
# Splitting the Sentence into list of words.
words = Sentence.split(" ")
# Reversing each word and creating
# a new list of words
# List Comprehension Technique
newWords = [word[::-1] for word in words]
# Joining the new list of words
# for a new Sentence
newSentence = " ".join(newWords)
return newSentence
I tried using and tinkering with code from websites like GeeksForGeeks and other websites but they ended up shifting the whole string by one amount, not shifting in two directions, doing some other weirdness, or a combination of those. I tried making my own system to no avail. I'm out of ideas. Maybe I'm missing something big but I am not sure.
Lets start with writing a function for encrypting a letter:
import string
letters = string.ascii_uppercase
def encrypt(letter, catalyst, is_odd):
letter_index = ord(letter) - ord("A")
new_letter_index = letter_index - catalyst if is_odd else letter_index + catalyst
return letters[new_letter_index]
Now it should be easy to use it correctly:
is_odd = True
encrypted = []
for character in new_mtbe_one:
# iterating over string iterates over every character
if character == " ":
is_odd = True
encrypted.append(" ")
continue
encrypted.append(encrypt(character, catalyst, is_odd))
is_odd = not is_odd
# catalyst stuff
In case to make it work with digits we need to improve slightly encrypt function:
digits = string.digits
def encrypt(letter, catalyst, is_odd):
value = ord("0") if letter.isdigit() else ord("A")
letter_index = ord(letter) - value
new_letter_index = letter_index - catalyst if is_odd else letter_index + catalyst
return digits[new_letter_index] if letter.isdigit() else letters[new_letter_index]
BONUS: Instead of getting day number from the input, you could get it from the current date:
def day_of_week():
return date.today().strftime("%w")

Encode and Decode a String using the repetition of characters along the string

I've been trying to tackle this problem but I haven't got any luck so far :( so any guidance would be awesome, especially since I'm just learning Python, so the problem is this:
Given a certain string, first you'll have to encode it using the following algorithm:
For every letter, strip the consecutive repetitions, if any, and add a number with the number of times it appears consecutively, including the first time.
After you've completed the encoding you'll have to create a function to decode it using the same criteria/assumptions.
Some examples of how it should work:
“Goooooooooddddd” => “G1o9d5”
“Oooo” => “O1o3”
“anagram” => “a1n1a1g1r1a1m1”
So far I have the following code for the encoding:
def encode(s) :
i = 0
while( i < len(s) - 1) :
count = 1
while s[i] == s[i + 1] :
i += 1
count += 1
if i + 1 == len(s):
break
print(str(s[i]) + str(count),
end = "")
i += 1
print()
# Code for the driver
if __name__ == "__main__" :
encode("Goooooooooddddd")
encode("Oooo")
With this I'm getting the needed output for the encoding part at least.
But I'm unable to actually do the "decoding" part by using this encoding as a starting point.
I know this question is quite silly/basic but I really can't get my head over it
Thanks in advance for the guidance/tips for this challenge problem
Decoding would be very simple in python since it allows character multiplication:
It might be like:
def decode(s):
a = list(s) # separate each character and number
i = 0
z = len(a)
while i < z-1: #This for block checks for any 2 digit numbers
print(i, a[i])
if a[i].isdigit() and a[i+1].isdigit():
a[i] = a[i]+a[i+1]
a.pop(i+1)
print(a)
z-=1
try:
if a[i+2].isdigit():
i-=1
except IndexError:
i-=1
i+=1
final_str = '' # the string that will have the value
for i in range(0, len(a), 2):
final_str += a[i]*int(a[i+1]) #a[i] is the character and int(a[i+1]) is the number of times.
# They are multiplied and then added to the final string
print(final_str) # The final string is printed
Thanks to #Tobi208 for pointing out the bug.
Decoding in pseudo code would involve:
Scan encoded string character by character
If character is alphabetic then add it to the decoded result
Sub scan encoded string for numeric sequence of 1 or more characters till next alphabetic character or end of string.
Repeat the last decoded string character this many times minus one.
Resume from step 1 till encoded string is entirely consumed.

How do I make it so the code only shifts the letters and not the space?

#get string and shift from user
string = input('Please enter a string to be ciphered: ')
shift = input('Please enter a shift amount between 0 and 25: ')
#strings are immutable so it must be converted to a list
s=list(string)
#now this will convert each character based on the shift
for i in range(0,len(s)):
s[i]=chr(ord(s[i]) + int(shift))
print ("".join(s))
You should call the method str.alpha to ensure that the chosen element is an alphabet before shifting
for i in range(0,len(s)):
if elem.isaplha():
s[i]=chr(ord(s[i]) + int(shift))
On a second though, you are doing to much work here. Why not use a comprehension expression?
s = ''.join(chr(ord(elem) + shift) if elem.isalpha() else elem for elem in s)
or if you are adventurous enough
s = ''.join([elem, chr(ord(elem) + shift)][elem.isalpha()] for elem in s)
and finally have you checked the string.makestrans along with str.translate to do the conversion?
from string import maketrans, ascii_alpha
s = s.translate(maketrans(ascii_alpha[shift:] + string.ascii_alpha[:shift])
All you have to do is check if the current character is not one you want to skip.
for i in range(0,len(s)):
#If not a space, cipher this character.
if s[i] != ' ':
s[i]=chr(ord(s[i]) + int(shift))
There is however, a possibility that one of your characters will be ciphered to a space, in which case that character would be skipped when reversing the cipher.
Also, a simple cipher like this should not be considered secure in the least.

How to make Caesar decoder circular in Python (2.7)?

After much frustration, I have made my first Caesar Decoder :)
But the problem now is to make the program circular...
For example if we want to shift doge by 1, no problem, it's ephf...
But what about xyz, and the shift was 4???
So programming pros help a first time novice aka newb out :P
Thanks...
import string
def main():
inString = raw_input("Please enter the word to be "
"translated: ")
key = int(raw_input("What is the key value? "))
toConv = [ord(i) for i in inString] #now want to shift it by key
toConv = [x+key for x in toConv]
#^can use map(lambda x:x+key, toConv)
result = ''.join(chr(i) for i in toConv)
print "This is the final result due to the shift", result
Here is Python code that I wrote to be easy to understand. Also, I think the classic Caesar cipher didn't define what to do with punctuation; I think the classic secret messages were unpunctuated and only contained letters. I wrote this to only handle the classic Roman alphabet and pass any other characters unchanged.
As a bonus, you can use this code with a shift of 13 to decode ROT13-encoded jokes.
def caesar_ch(ch, shift):
"""
Caesar cipher for one character. Only shifts 'a' through 'z'
and 'A' through 'Z'; leaves other chars unchanged.
"""
n = ord(ch)
if ord('a') <= n <= ord('z'):
n = n - ord('a')
n = (n + shift) % 26
n = n + ord('a')
return chr(n)
elif ord('A') <= n <= ord('Z'):
n = n - ord('A')
n = (n + shift) % 26
n = n + ord('A')
return chr(n)
else:
return ch
def caesar(s, shift):
"""
Caesar cipher for a string. Only shifts 'a' through 'z'
and 'A' through 'Z'; leaves other chars unchanged.
"""
return ''.join(caesar_ch(ch, shift) for ch in s)
if __name__ == "__main__":
assert caesar("doge", 1) == "ephf"
assert caesar("xyz", 4) == "bcd"
assert caesar("Veni, vidi, vici.", 13) == "Irav, ivqv, ivpv."
The part at the end is a "self-test" for the code. If you run this as a stand-alone program, it will test itself, and "assert" if a test fails.
If you have any questions about this code, just ask and I'll explain.
Just add the key to all the actual character codes, then if the added value is greater than z, modulo with character code of z and add it with the character code of a.
inString, key = "xyz", 4
toConv = [(ord(i) + key) for i in inString] #now want to shift it by key
toConv = [(x % ord("z")) + ord("a") if x > ord("z") else x for x in toConv]
result = ''.join(chr(i) for i in toConv)
print result # cde
I'd recommend using string.translate().
So, we can do the following:
key = 1
table = string.maketrans(string.ascii_lowercase + string.ascii_uppercase, string.ascii_lowercase[key:] + string.ascii_lowercase[:key] + string.ascii_uppercase[key:] + string.ascii_uppercase[:key])
And then we can use it as follows:
'doge'.translate(table) # Outputs 'ephf'
'Doge'.translate(table) # Outputs 'Ephf'
'xyz'.translate(table) # Outputs 'yza'
In particular, this doesn't change characters that are not ascii lowercase or uppercase characters, like numbers or spaces.
'3 2 1 a'.translate(table) # Outputs '3 2 1 b'
in general, to make something "wrap" you use the modulo function (% in Python) with the number you want to wrap, and the range you want it to wrap in. For example, if I wanted to print the numbers 1 through 10 a bajillion times, I would do:
i = 0
while 1:
print(i%10+1)
# I want to see 1-10, and i=10 will give me 0 (10%10==0), so i%10+1!
i += 1
In this case it's a little more difficult because you're using ord, which doesn't have a nice happy "range" of values. If you had done something like string.ascii_lowercase you could do...
import string
codex = string.ascii_lowercase
inString = "abcdxyz"
key = 3
outString = [codex[(codex.index(char)+key)%len(codex)] for char in inString]
However since you're using ord, we're kind of going from ord('A') == 65 to ord('z')==122, so a range of 0 -> 57 (e.g. range(58), with a constant of 65. In other words:
codex = "ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz"
# every char for chr(65) -> chr(122)
codex = ''.join([chr(i+65) for i in range(58)]) # this is the same thing!
we can do this instead, but it WILL include the characters [\]^_`
inString, key = 'abcxyzABCXYZ', 4
toConv = [(ord(i)+key-65)%58 for i in inString]
result = ''.join(chr(i+65) for i in toConv)
print(result)
# "efgBCDEFG\\]^"
I know this is kind of an old topic, but I just happened to be working on it today. I found the answers in this thread useful, but they all seemed to use a decision to loop. I figured a way to accomplish the same goal just using the modulus(remainder) operator (%). This allows the number to stay within the range of a table and loop around. It also allows for easy decoding.
# advCeaser.py
# This program uses a ceaser cypher to encode and decode messages
import string
def main():
# Create a table to reference all upper, lower case, numbers and common punctuation.
table = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz1234567890,.!?-#'
print 'This program accepts a message and a key to encode the message.'
print 'If the encoded message is entered with the negative value of the key'
print 'The message will be decoded!'
# Create accumulator to collect coded message
code =''
# Get input from user: Message and encode key
message = raw_input('Enter the message you would like to have encoded:')
key = input('Enter the encode or decode key: ')
# Loop through each character in the message
for ch in message:
# Find the index of the char in the table add the key value
# Then use the remainder function to stay within range of the table.
index = ((table.find(ch)+key)%len(table))
# Add a new character to the code using the index
code = code + table[index]
# Print out the final code
print code
main()
The encode and decode output look like this.
encode:
This program accepts a message and a key to encode the message.
If the encoded message is entered with the negative value of the key
The message will be decoded!
Enter the message you would like to have encoded:The zephyr blows from the east to the west!
Enter the encode or decode key: 10
croj0ozr92jlvy73jp2ywj4rojok34j4yj4roj7o34G
decode:
This program accepts a message and a key to encode the message.
If the encoded message is entered with the negative value of the key
The message will be decoded!
Enter the message you would like to have encoded:croj0ozr92jlvy73jp2ywj4rojok34j4yj4roj7o34G
Enter the encode or decode key: -10
The zephyr blows from the east to the west!
Sorry if my formatting looks catywompus I literally found stackoverflow yesterday! Yes, I literally mean literally :)

Decrypting Input?

This is my assignment:
Write a program which DECRYPTS secret messages.
It should first prompt the user for the scrambled alphabet. It then should ask for the secret message. Finally, it outputs the unscrambled version.
Note that there are exactly 26 characters input for the scrambled alphabet. All alphabetic characters are translated into their decoded equivalents (which will take a WHILE loop), and all other, non-alphabetic characters should be output exactly as they were without translation.
This is my code so far:
decrypt = ["*"] * 26
scram_alphabet = input("Please input the scrambled alphabet in order: ")
while len(scram_alphabet) != 26:
scram_alphabet = input("Please input the scrambled alphabet in order. The alphabet must have 26 characters: ")
num = 0
for each_letter in scram_alphabet:
decrypt[num] = ord(each_letter)
num = num + 1
print()
print("Your scrambled alphabet is: ", end = "")
for num in range (26):
print(chr(decrypt[num]), end = "")
print()
print()
msg = input("Now input your scrambled message: ")
print()
print()
alphabet = 65
for s in range (26):
decrypt[s] = (alphabet)
alphabet = alphabet + 1
print("The unscrambled alphabet is: ", end = "")
for num in range (26):
print(chr(decrypt[num]), end = "")
print()
print()
print("Your unscrambled message reads: ")
for alpha in msg.upper():
if alpha < "A" or alpha > "Z":
print(alpha, end="")
else:
ord_alpha = ord(alpha)
print (chr(decrypt[ord_alpha - 65]), end = "")
Ex: Scrambled Alphabet = XQHAJDENKLTCBZGUYFWVMIPSOR , Scrambled Message = VNKW KW BO 1WV WJHFJV BJWWXEJ!
Everything works fine until I get to the last print statement, where it says that the unscrambled message is the same as the scrambled message. I know that the instructions call for a while loop but I couldn't figure out how to use one to decode the alphabet.
Any helpers?
Well, as has already been noted, you are clobbering your decrypt variable.
However, you are also not building the mapping from the 'scrambled' alphabet to the regular one properly / at all.
Decrypting without using anything more than basic iteration over lists and simple list functions (index()) might look something like this:
cleartext = ""
for c in msg:
if c in alphabet:
pos = alphabet.index(c)
cleartext += string.ascii_uppercase[pos]
else:
cleartext += c
Rather than just patching your code, it could benefit from some improvement. I gather this is probably homework and you are probably not expected to go this far, but IMO there is nothing wrong with learning it anyway.
You are not checking that the input alphabet only contains whatever you consider to be legal values (e.g. probably A-Z in this case), nor are you checking for duplicates. The user could input any old rubbish and break your program otherwise.
Your printing and looping is not very idiomatic.
Functions are good for breaking up your code into more easily readable and maintainable pieces.
This may come across as old school or pedantic, but lines longer than 80 characters are not recommended style for Python. Adjacent string literals (e.g "one""two") will be joined (even across newlines).
If I had to do what you're doing without translate (see below), I might do something like this (just a quick example, could probably be improved with a bit of work):
import string
def validate(alpha):
# Is it exactly 26 characters long?
if len(alpha) != 26: return False
for c in alpha:
# Is every character in [A-Z]?
if c not in string.ascii_uppercase: return False
# Is this character duplicated?
if alpha.count(c) > 1: return False
return True
alphabet = ""
while not validate(alphabet):
alphabet = input("Please input the encryption alphabet in order (only A-Z"
" allowed, with no duplicates): ")
msg = input("Now input your encrypted message: ")
print("Your encrypted alphabet is:", alphabet)
print("Your encrypted message is:", msg)
# Create a mapping from one alphabet to the other using a dictionary
table = dict(zip(alphabet, string.ascii_uppercase))
cleartext = "".join([table[c] if c in table else c for c in msg])
print("Your decrypted message reads:", cleartext)
Lastly, you could also do this using Python's builtin string translation, like so:
import string
# read and validate alphabet
# read message
print(str.translate(message, str.maketrans(alphabet, string.ascii_uppercase)))
You are clobbering decrypt after you wrote all the scrambled letters into it:
crypt = ["*"] * 26
for s in range (26):
val = decrypt[s] - 65
crypt[val] = (s + 65)

Categories