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
Related
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)
As part of the CS50 Harvard programming course, which I'm currently attending, I'm doing an exercise called "Vigenere".
The Cipher of Vigenere is supposed to have a key as input, for example "abcd". This will encode any plaintext, according to each character in the key, where a = 0 and b = 1 etc. So a key "abcd" with plaintext "aa!aa" would give "ab!cd". if the plaintext is longer than the key, the key is supposed to loop back to [0] and start over until plaintext has all been encoded. Non alphabetic letters are supposed to printed out normally however.
My program is doing everything right (it's going line by line and the expected behavior is met) except when I receive input that starts with an uppercase letter followed by a lowercase letter my program prints a different letter with the lowercase key then it should give me. EX: key: "Baz". Plaintext: "aaa". Result: "bgz" where it should return "baz".
Have been googling, debugging but just can't figure it out. Have tried doing it in a lot of other different ways too but I just can't get it to work. (sorry for kind of copy paste, as you might notice I've already posted a similar problem, however that was in C (this is python) and it was a different kind of bug)
Code:
import sys
if len(sys.argv) != 2 or not sys.argv[1].isalpha():
print("usage: python vigenere.py keyword")
sys.exit()
cipher = sys.argv[1]
plaintext = input("plaintext: ")
j = 0
def code(j):
for key in cipher:
if key.islower():
return ord(cipher[j]) - 97
if key.isupper():
return ord(cipher[j]) - 65
print("ciphertext: ", end="")
for letters in plaintext:
if letters.islower():
print(chr(((ord(letters) - 97 + code(j)) % 26) + 97), end="")
j += 1
if letters.isupper():
print(chr(((ord(letters) - 65 + code(j)) % 26) + 65), end="")
j += 1
if j == len(cipher):
j = 0
if not letters.isalpha():
print(letters, end="")
print("")
The problem in your code is caused by your code function.
In it, you use the line for key in cipher:, before checking it with if key.islower(): or if key.isupper():.
The problem is that every time we enter the code function, due to the for loop we only check if the first letter in the cipher is upper or lower case.
eg. for cipher 'Baz', j = 0, we check if B is upper/lower, and get upper. We immediately return uppercase B
For cipher 'Baz', j = 1, we check if B is upper/lower, and get upper. We immediately return upper A. (When we should be checking a for upper/lower, and returning lower a!)
This problem is fixed by checking the right letter of the cipher for upper or lower case, and can be fixed by replacing for key in cipher: with key = cipher[j], like in the block below:
def code(j):
key = cipher[j]
if key.islower():
return ord(key) - 97
if key.isupper():
return ord(key) - 65
After making the change, for the following inputs:
cipher = "BazBgh"
plaintext = "aaaaAa"
We get
ciphertext: bazbGh
I am new to Python and decided to make my own Caesar cipher encryptor. I've made the encrypter and it is ok, however, the decrypter can only successfully decrypt one word. If I enter a sentence, it merges the decryption all together. Is there an easy fix for this?
def decrypt():
ciphertext = raw_input('Please enter your Encrypted sentence here:')
shift = input('Please enter its shift value: ')
space = []
cipher_ords = [ord(x) for x in ciphertext]
plaintext_ords = [o - shift for o in cipher_ords]
plaintext_chars = [chr(i) for i in plaintext_ords]
plaintext = ''.join(plaintext_chars)
print 'Decryption Successful'
print ""
print 'Your encrypted sentence is:', plaintext
decrypt()
What I propose is to split your raw_input() at every space, iterate over each word in the split input, and then join the sentence back together with spaces. It seems to be the most canonical solution I could think of:
def decrypt():
ciphertext = raw_input('Please enter your Encrypted sentence here:')
shift = int(raw_input('Please enter its shift value: '))
space = []
# creat a list of encrypted words.
ciphertext = ciphertext.split()
# creat a list to hold decrypted words.
sentence = []
for word in ciphertext:
cipher_ords = [ord(x) for x in word]
plaintext_ords = [o - shift for o in cipher_ords]
plaintext_chars = [chr(i) for i in plaintext_ords]
plaintext = ''.join(plaintext_chars)
sentence.append(plaintext)
# join each word in the sentence list back together by a space.
sentence = ' '.join(sentence)
print 'Decryption Successful\n'
print 'Your encrypted sentence is:', sentence
decrypt()
Output:
Please enter your Encrypted sentence here: lipps xlivi
Please enter its shift value: 4
Decryption Successful
Your encrypted sentence is: hello there
Notes:
Never just do input() in Python 2.x because it uses eval() implicitly - which can be very dangerous. Use int(raw_input()) instead.
I removed the extra print statement you had to create a new line. Append a new line to your second print statement instead.
Based on your comment about "hello there" as input, I suspect that the issue has to do with unprintable ascii characters. You are missing two crucial parts of your Caesar cypher.
For the first issue, consider:
>>> chr(ord(' ') - 4)
'\x1c'
Oh no! 4 characters to the left of the space (32) is the...ASCII file separator! How did Caesar fit that on a clay tablet?
For the second issue:
>>> chr(ord('A') - 4)
'='
The 'A' should wrap around in a true Caesar cypher, but instead you are exploring the hinterlands (well, not really) of non-alphabetic ASCII codes.
You thus need to include two important steps:
Exclude non-alphabetic characters from the Caesar cypher.
Make sure that letters wrap when approach the end: A - 1 should equal Z.
Your probably wanted not to decrypt the space character as in your "encrypted" text it is not encrypted. If this is the case, here is the modified part of your code:
cipher_ords = [ord(x) if x != " " else -1 for x in ciphertext]
plaintext_ords = [o - shift if o != -1 else -1 for o in cipher_ords]
plaintext_chars = [chr(i) if i != -1 else " " for i in plaintext_ords]
(Let cipher_ords has -1 for each space symbol and consequenlty in plaintext_ords, too. In plaintext_chars this -1 will return back to the original space symbol.)
My question is how to improve the code so that it can adapt to however long the input message is. As is, the message must be 5 letters. I would like to improve the code such that a message of any length can be inputted and the cipher will work with it. Help would be much appreciated. :-) See the code below!
#Enter your message
message=raw_input('Enter your message here. Make sure to use all CAPS througout your message and leave no spaces in between words.')
length=len(message)
print 'The length of your message is ',length
#This statement is a possible idea to let the program know how many letters it will be need to shift. But I don't know how to actually do this.
print chr(length+64)
#Indexes letters out of message.
A=message[length-length]
B=message[length-length+1]
C=message[length-length+2]
D=message[length-length+3]
E=message[length-length+4]
#Shifts letters and accounts for shifting XYZ to ABC.
def shift(x):
if ord(x)+3==91:
return 65
if ord(x)+3==92:
return 66
if ord(x)+3==93:
return 67
else:
return ord(x)+3
a2=shift(A)
b2=shift(B)
c2=shift(C)
d2=shift(D)
e2=shift(E)
#Converts shifted ordinals back to characters
def convert(x):
return chr(x)
first=convert(a2)
second=convert(b2)
third=convert(c2)
fourth=convert(d2)
fifth=convert(e2)
#Prints resultant characters
print first,second,third,fourth,fifth
import string
shift_amt = 13
alphabet_lc = string.ascii_lowercase
shifted_lc = alphabet_lc[shift_amt:]+alphabet_lc[:shift_amt]
alphabet_uc = alphabet_lc.upper()
shifted_uc = shifted_lc.upper()
trans_tab = string.maketrans(alphabet_lc+alphabet_uc,shifted_lc+shifted_uc)
message = "Encode Me To a new MessaGez!"
print message.translate(trans_tab)
is one way of doing it in Python2 at least
Use two for loops, one for looping through each character, and one for shifting the character the desired amount of times. We use a function upper() to shift a character.
def upper(char):
from string import ascii_letters as _all
if char == ' ':
return ' '
return _all[_all.index(char)+1] if char != 'Z' else 'a'
def shift(message, key):
temp = []
for i in message:
char = i
for k in range(key):
char = upper(char)
temp.append(char)
return ''.join(temp)
message=raw_input('Enter your message here: ')
key = int(raw_input('Enter the desired key: '))
length=len(message)
print 'The length of your message is', length
print 'Your encrypted message is {0}'.format(shift(message, key))
This runs as:
bash-3.2$ python caesar.py
Enter your message here: This works WITH or without CAPS
Enter the desired key: 10
The length of your message is 31
Your encrypted message is drsC GyBuC gSdR yB GsDryED MKZc
bash-3.2$
The Ceasar cipher is built in in python 2;
In [6]: 'The Caesar cipher is built-in.'.encode('rot13')
Out[6]: 'Gur Pnrfne pvcure vf ohvyg-va.'
As you can see, this encoding only acts on letters, and it works for upper and lower case.
But is you want to remove spaces and make every thing upper-case, Python can do that as well;
In [9]: 'this is a string'.translate(None, ' \t')
Out[9]: 'thisisastring'
In [10]: 'this is a string'.translate(None, ' \t').upper()
Out[10]: 'THISISASTRING'
In [11]: 'this is a string'.translate(None, ' \t').upper().encode('rot13')
Out[11]: 'GUVFVFNFGEVAT'
Or in a different way;
In [15]: ''.join('this is a string'.split()).upper().encode('rot13')
Out[15]: 'GUVFVFNFGEVAT'
Bibliography:
Kid Snippets: "Math Class" (Imagined by Kids) - YouTube http://youtu.be/KdxEAt91D7k
Mary Had A Little Lamb Nursery Rhyme With Lyrics - YouTube http://youtu.be/CkRdvGmcCBE
Mary Had a Little Lamb - Wikipedia, the free encyclopedia http://goo.gl/FNEuyd
Python source code:
Note: working for negative shift numbers also
Note: if reverse shift then we do encode - decode message
Note: preserving spaces also
small_chars = [chr(item) for item in range(ord('a'), ord('z')+1)]
upper_chars = [item.upper() for item in small_chars]
def encode_chr(chr_item, is_upper_case):
'''
Cipher each chr_item.
'''
# setting orig and end order.
if is_upper_case:
orig_ord = ord('A')
end_ord = ord('Z')
else:
orig_ord = ord('a')
end_ord = ord('z')
# calculating shift
temp_ord = ord(chr_item)+shift
# calculating offset order with modulo.
# char is after end_ord, calculating offset
num_of_chars = 26
offset_ord = (temp_ord - end_ord - 1)%num_of_chars
return chr(orig_ord + offset_ord)
# enable while loop to repeat until status not 'y'
status = 'y'
while status == 'y':
# enter word to cipher.
word = raw_input("Word: ")
# enter char shift
shift = input("Shift: ")
print
# create cipher list variable
cipher = list()
# loop trough each char in word
for chr_item in word:
# encode just letters.
# replace non-alfa with underscore: "_"
if chr_item in upper_chars or chr_item in small_chars:
# set is_uppser_case to True for upper case chars.
is_upper_case = (chr_item in upper_chars) and True
# cipher char.
temp_chr = encode_chr(chr_item, is_upper_case)
# append ciphered char to list
cipher.append(temp_chr)
elif chr_item is ' ':
cipher.append(chr_item)
else:
cipher.append('_')
# print word
print word
# print ciphered word
print ''.join(cipher)
# repeat again for another word?
status = raw_input("Repeat? [y|n]: ")
print
Test cases:
>>>
Word: aAzZ!#
Shift: 1
aAzZ!#
bBaA__
Repeat? [y|n]: y
Word: aAzZ#!
Shift: -1
aAzZ#!
zZyY__
Repeat? [y|n]: y
Word: aAzZ#$
Shift: 27
aAzZ#$
bBaA__
Repeat? [y|n]: y
Word: aAzZ%^
Shift: -27
aAzZ%^
zZyY__
Repeat? [y|n]: n
>>>
Output:
Note: if reverse shift then we do encode - decode message
>>>
Word: "Mary Had a Little Lamb"
Shift: 1
"Mary Had a Little Lamb"
_Nbsz Ibe b Mjuumf Mbnc_
Repeat? [y|n]: y
Word: _Nbsz Ibe b Mjuumf Mbnc_
Shift: -1
_Nbsz Ibe b Mjuumf Mbnc_
_Mary Had a Little Lamb_
Repeat? [y|n]: n
>>>
Here is a simple Caesar cipher program written for Python 3 that should not be very difficult to rewrite for Python 2:
import string
def main():
key = 5
table = str.maketrans(string.ascii_letters,
string.ascii_lowercase[key:] +
string.ascii_lowercase[:key] +
string.ascii_uppercase[key:] +
string.ascii_uppercase[:key])
plaintext = input('Please enter a phrase: ')
ciphertext = plaintext.translate(table)
print('Your encrypted phrase is:', ciphertext)
if __name__ == '__main__':
main()
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!