I struck a brick wall trying to solve this and I am not sure how to approach this problem.
My idea is to compare both first characters of each string and if they are the same, save the character in the alphabet string of the index position shift.
Recurse the rest of the string by removing the first character of the secret. If the first characters are different, recurse but removing the first character of the alphabet string.
I am not sure how to recurse for the rest of the alphabet though.
alphabet = "abcdefghijklmnopqrstuvwxyz"
def caesar_encrypt(secret, shift):
if len(secret) == 0:
return ""
elif shift == 0:
return secret
else:
if secret[0] == alphabet[0]:
return alphabet[shift] + caesar_encrypt(secret[1:],shift)
else:
return caesar_encrypt(secret,shift), alphabet[1:]
I would suggest using str.index to find the index of where each character is in the alphabet for each character in your string. After, use this to index into the alphabet and recurse.
A couple of gotchas that you should look out for:
If the string contains a space, place this space as is in the string then move onto the next character
You need to handle wraparound where if we're at the end of the alphabet and you choose a shift that would exceed the end of the alphabet, you need to wrap around and go to the beginning of the alphabet.
This should work:
alphabet = "abcdefghijklmnopqrstuvwxyz"
def caesar_encrypt(secret, shift):
if len(secret) == 0:
return ""
elif shift == 0:
return secret
elif secret[0] == ' ': # New - handle spaces
return ' ' + caesar_encrypt(secret[1:], shift)
else:
index = (alphabet.index(secret[0]) + shift) % len(alphabet) # Find the right place to access the alphabet
return alphabet[index] + caesar_encrypt(secret[1:], shift) # Use this new character then recurse
NB: This implementation only handles lowercase letters.
How about this:
def shift_alphabet(shift):
return alphabet[shift:] + alphabet[:shift]
def caesar_encrypt(secret, shift):
coded_alphabet = shift_alphabet(shift)
coded = [coded_alphabet[alphabet.index(i)] for i in secret]
coded = ''.join(coded)
return coded
Using map/lambda instead of for:
def shift_alphabet(shift):
return alphabet[shift:] + alphabet[:shift]
def encrypt_letter(letter, coded_alphabet):
return coded_alphabet[alphabet.index(letter)]
def caesar_encrypt(secret, shift):
coded_alphabet = shift_alphabet(shift)
coded = map(lambda x: encrypt_letter(x, coded_alphabet), secret)
coded = ''.join(coded)
return coded
Related
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!
So I'm trying to do a code that will shift every letter in a word back by a number of letters in the alphabet (wrapping around for the end). For example, if I want to shift by 2 and I input CBE, I should get AZC. or JOHN into HMFL. I got a code to work for only one letter, and I wonder if there's a way to do a nested for loop for python (that works?)
def move(word, shift):
alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZ"
original = ""
for letter in range(26, len(alphabet)):
if alphabet[letter] == word: #this only works if len(word) is 0, I want to be able to iterate over the letters in word.
original += alphabet[letter-shift]
return original
You could start like this
def move(word, shift):
alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
return "".join([alphabet[alphabet.find(i)-shift] for i in word])
Basically, this list comprehension constructs a list of the single letters. Then, the index of the letter in the alphabet is found by the .find method. The (index - shift) is the desired new index, which is extracted from alphabet. The resulting list is joined again and returned.
Note that it does obviously not work on lowercase input strings (if you want that use the str.upper method). Actually, the word should only consist of letters present in alphabet. For sentences the approach needs to treat whitespaces differently.
Don't find the letter in the alphabet that way -- find it with an index operation. Let char be the letter in question:
alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
...
char_pos = alphabet.index(char)
new_pos = (char_pos - shift) % len(alphabet)
new_char = alphabet[new_pos]
Once you understand this, you can collapse those three lines to a single line.
Now, to make it operate on an entire word ...
new_word = ""
for char in word:
# insert the above logic
new_word += new_char
Can you put all those pieces together?
You'll still need your check to see that char is a letter. Also, if you're interested, you can build a list comprehension for all the translated characters and the apply ''.join() to get your new word.
For instance ...
If the letter is in the alphabet (if char in alphabet), shift the given distance and get the new letter, wrapping around the end if needed (% 26). If it's not a capital letter, then use the original character.
Make a list from all these translations, and then join them into a string. Return that string.
def move(word, shift):
alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
return ''.join([alphabet[(alphabet.find(char) - shift) % 26]
if char in alphabet else char
for char in word])
print move("IBM", 1)
print move("The 1812 OVERTURE is COOL!", 13)
Output:
HAL
Ghe 1812 BIREGHER is PBBY!
A_VAL = ord('a')
def move(word, shift):
new_word = ""
for letter in word:
new_letter = ord(letter) - shift
new_word += chr(new_letter) if (new_letter >= A_VAL) else (26 + new_letter)
return new_word
Note that this will only work for lowercase words. As soon as you start mixing upper and lowercase letters you'll need to start checking for them. But this is a start. I discarded your nested loop idea because you should avoid those if at all possible.
You could use : chr() give you the character for a ascii number, ord() give you the ascii number for the matching character.
Here is an old Vigenere project :
def code_vigenere(ch,cle):
text = ch.lower()
clef = cle.lower()
L = len(cle)
res = ''
for i,l in enumerate(text):
res += chr((ord(l) - 97 + ord(cle[i%L]) - 97)%26 +97)
return res
I am working on a program in Python that sort-of encrypts a message. I have converted the string (which will vary based on user input) to a list and have a number. I want to automatically change each letter in the list with the number. For example, if the number is three, every A changes into a D. I have a dictionary with the vales for letters, such as {"a" : 1}, {"b" : 2}, etc.
I can not seem to get how I could change the letters (without knowing what they are) and possibly in accordance with my dictionary.
What I have so far (dictionary is somewhere else):
def numtolist(n):
seedstring = str(n)
numlist = []
for digit in seedstring:
numlist.append(int(digit))
return numlist
currentnumber = seed^2
newmessage = str()
for letter in messageList:
numtolist(currentnumber)
num1 = numlist[0]
If the transform is as simple as an alphabetic shift you can simply do it by:
if original_character.isupper(): # Determine whether it is upper/lower case
base_a = 'A' # use 'A' as a base for uppercase
else: #
base_a = 'a' # use 'a' as a base for lowercase
orig_char_ascii = ord(original_character) # Get the original ascii value
orig_char_alpha_index = orig_char_ascii - ord(base_a) # Get the `0-25` alphabetic
# index of the character
shift_number = 3 # Set the amount to shift by
new_char_alpha_index = orig_char_alpha_index + shift_number # Add the shift value
new_char_alpha_index = new_char_alpha_index % 26 # Take the modulus to impose
# periodic boundary conditions
# i.e. if you had 'z' + 3
# 'z' + 3 -> 25 + 3 -> 28
# 28 % 26 = 2, 2 -> 'c'
new_char_ascii_index = ord(base_a) + new_char_alpha_index # scale back to the ascii
# value
new_char = chr(new_char_ascii_index)
The basic idea is that each character corresponds to an ascii number which can be gotten by ord (i.e. ord('a') = 97). The chr method reverses this: chr(97) = 'a'.
The method is briefly: you get the ascii value, scale to a 0-25 alphabetic range, add your shift, wrap values that overflow the alphabetic range, scale back to ascii, and get a character back via chr.
You can compact this method a lot, I was verbose in the name of education:
def shift_char(ch,shift):
if not ch.isalpha():
return ch # return non-alphabetic characters as is
is_upper = 'A' <= original_character <= 'Z'
if is_upper:
base_a = 'A'
else:
base_a = 'a'
return chr(ord(base_a) + (ord(ch)-ord(base_a)+shift)%26)
If you wanted to use the same random shift for a whole string you could split this into a couple methods:
def shift_char(ch,shift):
if not ch.isalpha():
return ch # return non-alphabetic characters as is
if ch.isupper():
base_a = 'A'
else:
base_a = 'a'
return chr(ord(base_a) + (ord(ch)-ord(base_a)+shift)%26)
def shift_string(s,shift):
return ''.join(shift_char(i,r) for i in s)
If you want to be able to decode it later you need a reversal method and to store the shift value:
def shift_char(ch,shift):
if not ch.isalpha():
return ch # return non-alphabetic characters as is
if ch.isupper():
base_a = 'A'
else:
base_a = 'a'
return chr(ord(base_a) + (ord(ch)-ord(base_a)+shift)%26)
def encode_string(s,shift):
return ''.join(shift_char(i,r) for i in s), shift # NOTE EXTRA return value
def decode_string(s,shift):
return ''.join(shift_char(i,-shift) for i in s)
# usage
s = 'Hello, StackOverflow!'
enc_s,shift = encode_string(s)
dec_s = decode_string(enc_s,shift)
If your shift is random you can just pass that as the argument:
import random
def shift_char(ch,shift):
if not ch.isalpha():
return ch # return non-alphabetic characters as is
if ch.isupper():
base_a = 'A'
else:
base_a = 'a'
return chr(ord(base_a) + (ord(ch)-ord(base_a)+shift)%26)
def encode_string(s,shift):
return ''.join(shift_char(i,shift) for i in s)
def decode_string(s,shift):
return ''.join(shift_char(i,-shift) for i in s)
# usage
s = 'Hello, StackOverflow!'
shift = random.randint(1,25)
enc_s = encode_string(s,shift)
dec_s = decode_string(enc_s,shift)
or rearrange the methods
import random
def shift_char(ch,shift):
if not ch.isalpha():
return ch # return non-alphabetic characters as is
if ch.isupper():
base_a = 'A'
else:
base_a = 'a'
return chr(ord(base_a) + (ord(ch)-ord(base_a)+shift)%26)
def encode_string(s):
shift = random.randint(1,25)
return ''.join(shift_char(i,shift) for i in s), shift
def decode_string(s,shift):
return ''.join(shift_char(i,-shift) for i in s)
# usage
s = 'Hello, StackOverflow!'
enc_s, shift = encode_string(s)
dec_s = decode_string(enc_s,shift)
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 :)
I tried this: Capitalize a string. Can anybody provide a simple script/snippet for guideline?
Python documentation has capitalize() function which makes first letter capital. I want something like make_nth_letter_cap(str, n).
Capitalize n-th character and lowercase the rest as capitalize() does:
def capitalize_nth(s, n):
return s[:n].lower() + s[n:].capitalize()
my_string[:n] + my_string[n].upper() + my_string[n + 1:]
Or a more efficient version that isn't a Schlemiel the Painter's algorithm:
''.join([my_string[:n], my_string[n].upper(), my_string[n + 1:]])
x = "string"
y = x[:3] + x[3].swapcase() + x[4:]
Output
strIng
Code
Keep in mind that swapcase will invert the case whether it is lower or upper.
I used this just to show an alternate way.
This is the comprehensive solution: either you input a single word, a single line sentence or a multi line sentence, the nth letter will be converted to Capital letter and you will get back the converted string as output:
You can use this code:
def nth_letter_uppercase(string,n):
listofwords = string.split()
sentence_upper = ''
for word in listofwords:
length = len(word)
if length > (n - 1):
new_word = word[:n-1] + word[n-1].upper() + word[n:]
else:
new_word = word
sentence_upper += ' ' + new_word
return sentence_upper
calling the function defined above (I want to convert 2nd letter of each word to a capital letter):
string = '''nature is beautiful
and i love python'''
nth_letter_uppercase(string,2)
output will be:
'nAture iS bEautiful aNd i lOve pYthon'
I know it's an old topic but this might be useful to someone in the future:
def myfunc(str, nth):
new_str = '' #empty string to hold new modified string
for i,l in enumerate(str): # enumerate returns both, index numbers and objects
if i % nth == 0: # if index number % nth == 0 (even number)
new_str += l.upper() # add an upper cased letter to the new_str
else: # if index number nth
new_str += l # add the other letters to new_str as they are
return new_str # returns the string new_str
A simplified answer would be:
def make_nth_letter_capital(word, n):
return word[:n].capitalize() + word[n:].capitalize()
You can use:
def capitalize_nth(text, pos):
before_nth = text[:pos]
n = text[pos].upper()
new_pos = pos+1
after_nth = text[new_pos:]
word = before_nth + n + after_nth
print(word)
capitalize_nth('McDonalds', 6)
The outcome is:
'McDonaLds'
I think this is the simplest among every answer up there...
def capitalize_n(string, n):
return string[:n] + string[n].capitalize() + string[n+1:]
This works perfect