I am coding a Caesar cipher. The key is an integer from 1 to 25. This cipher rotates the letters of the alphabet (A to Z). The encoding replaces each letter
with the 1st to 25th next letter in the alphabet (wrapping Z to A). So key 2 encrypts “HI” to “JK”, but key 20 encrypts “HI” to “BC”.
But If I put in
"I am super" it will output "k kc oouwrgt" when it should be "k co uwrgt" with a key of 2. It will also not go back to the beginning of the alphabet e.g 'x' will not go to 'a' with a key of 2. I use python 3.4.1
encode = []
a = "abcdefghijklmnopqrstuvwyxz"
a = list(a)
print(a)
e = input("encode or decode --->")
text = input("Sentence -->").lower()
text = list(text)
print(text)
Key = int(input("Key -->"))
if Key > 25:
print("Too high")
else:
print(Key)
if e == "encode":
for i, item in enumerate(text):
if item == " ":
encode.append(letter)
else:
num = a.index(item)
num = num + int(Key)
letter = a[num]
encode.append(letter)
for i in range(len(encode)):
print(encode[i])
When you encounter a space, you append the last letter again, instead of item:
if item == " ":
encode.append(letter)
This causes k and o to appear twice when the key is 2; you re-appended the encoded i -> k and m -> o results.
You need to use the % modulo operator to make your index 'wrap round':
num = (num + Key) % 26
I removed the int() call, you already turned Key to an integer earlier.
Other tips:
You don't need to turn a into a list; strings are sequences too and support indexing and the .index()method directly. The same applies to text; just loop over the string itself.
You are not using i in the for i, item in enumerate(text): loop; drop enumerate altogether: for item in text:.
You could just print your encoded characters directly in that loop, no need to use an encode list and a separate loop.
The str.join() method would let you print your encoded text all on one line: print(''.join(encode)) instead of your last for loop.
The absolute fastest method of encoding a string is to use a translation table, a dictionary mapping input characters to output characters, and the str.translate() method. You can use the str.maketrans() function to make that table:
import string
a = string.ascii_lowercase # why type yourself when the stdlib has these?
text = input("Sentence -->").lower()
Key = int(input("Key -->"))
mapping = str.maketrans(a, a[Key:] + a[:Key]) # letters to rotated letters
print(text.translate(mapping))
The trick lies in creating the second string for str.maketrans(); using slicing it is easy to create a rotated string, by taking everything from position Key onwards, and the first Key characters at the end:
>>> a[Key:] + a[:Key]
'cdefghijklmnopqrstuvwyxzab'
One obvious solution would be to use modulo for the alphabet index:
The % (modulo) operator yields the remainder from the division of the
first argument by the second.
>>> 12 % 26
12
>>> 26 % 26
0
>>> 28 % 26
2
As a bonus, you wouldn't need to check the key is lower than 25, and you'll never get an IndexError: list index out of range.
encode = []
a = "abcdefghijklmnopqrstuvwyxz"
e = input("encode or decode --->")
text = input("Sentence -->").lower()
key = int(input("Key -->"))
if e == "encode":
for i, item in enumerate(text):
if item == " ":
encode.append(item)
else:
num = a.index(item)
num = num + int(key)
letter = a[num % 26]
encode.append(letter)
for letter in encode:
print(letter)
A few notes:
a string is already an iterable of characters. No need to convert it to a list
letter wasn't defined in if item == " "
to iterate over a list, you don't need the length or the index.
to decode the message, just change the sign of the key. It should work just like encode, thanks to modulo : -2 % 26 # => 24
As an example:
encode or decode --->encode
Sentence -->i am super
Key -->2
k
c
o
u
w
r
g
t
and
encode or decode --->encode
Sentence -->k co uwrgt
Key -->-2
i
a
m
s
u
p
e
r
Related
input: ['baNaNa', 7] # string and step size
required output : 'utGtGt' # every character of string shifted backwards by step size
import ast
in_string = input()
lis = ast.literal_eval(in_string)
st = lis[0]
step = lis[1]
alphabets = 'abcdefghijklmnopqrstuvwxyz'
password = ''
for letter in st:
if letter in alphabets:
index_val = alphabets.index(letter) - (step)
password += alphabets[index_val]
print(password)
Output i am getting is 'utgtgt'. I want 'utGtGt'. Help on this would be appreciated a lot.
The string module has methods to create a transformation dictionary and a translate method to do exactly what you want:
st = "baNaNa"
step = 7
alphabets = 'abcdefghijklmnopqrstuvwxyz'
alph2 = alphabets.upper()
# lower case translation table
t = str.maketrans(alphabets, alphabets[-step:]+alphabets[:-step])
# upper case translation table
t2 = str.maketrans(alph2, alph2[-step:]+alph2[:-step])
# merge both translation tables
t.update(t2)
print(st.translate(t))
Output:
utGtGt
You give it the original string and an equal long string to map letters to and apply that dictionary using str.translate(dictionary).
The sliced strings equate to:
print(alphabets)
print(alphabets[-step:]+alphabets[:-step])
abcdefghijklmnopqrstuvwxyz
tuvwxyzabcdefghijklmnopqrs
which is what your step is for.
See Understanding slice notation if you never saw string slicing in use.
by processing each charater and checking it's cardinal no and making calculation accordingly help you to reach the result
def func(string, size):
if size%26==0:
size=26
else:
size=size%26
new_str = ''
for char in string:
if char.isupper():
if ord(char)-size<ord('A'):
new_str+=chr(ord(char)-size+26)
else:
new_str+=chr(ord(char)-size)
elif char.islower():
if ord(char)-size<ord('a'):
new_str+=chr(ord(char)-size+26)
else:
new_str+=chr(ord(char)-size)
return new_str
res =func('baNaNa', 7)
print(res)
# output utGtGt
Here's a simple solution that makes use of the % modulo operator to shift letters backwards.
It basically collects all of the letters in a reverse index lookup dictionary, so looking up letter positions is O(1) instead of using list.index(), which is linear O(N) lookups.
Then it goes through each letter and calculates the shift value from the letter index e.g. for the letter a with a shift value of 7, the calculation will be (0 - 7) % 26, which will give 19, the position of u.
Then once you have this shift value, convert it to uppercase or lowercase depending on the case of the original letter.
At the end we just str.join() the result list into one string. This is more efficient than doing += to join strings.
Demo:
from string import ascii_lowercase
def letter_backwards_shift(word, shift):
letter_lookups = {letter: idx for idx, letter in enumerate(ascii_lowercase)}
alphabet = list(letter_lookups)
result = []
for letter in word:
idx = letter_lookups[letter.lower()]
shifted_letter = alphabet[(idx - shift) % len(alphabet)]
if letter.isupper():
result.append(shifted_letter.upper())
else:
result.append(shifted_letter.lower())
return ''.join(result)
Output:
>>> letter_backwards_shift('baNaNa', 7)
utGtGt
I would probably go with #Patrick Artner's pythonic solution. I just showed the above implementation as a learning exercise :-).
I have to create a program that gets a string, and an integer n; it will increment each character of the string by n characters; for example, if the string is "abc" and n=1, the output would be "bcd", if n=2, it'd be "cde".
So far I have written this code
string = list( input( "Insert a string, it will be codified: " ) )
n = int( input( "Insert an integer, each string's character will be increased by that number: " ) )
for characterIndex in range( len( string ) ):
string[characterIndex] = chr( ord( string[characterIndex] ) + n )
print( ''.join( string ) )
Nonetheless, if I input "xyz" and n=1, I get "yz{", which makes sense since ascii's next character to "z" is "{". You can imagine that for a higher n, it gets worse; I have been trying to solve this problem for any n using modulo, tried to take advantage from the fact that there are 26 letters, but I'm still unable to find a mathematical increment that detects when the string has been incremented further than "z", so it "gets back" to "a".
Any recommendations? Thanks in advance.
It's kind of cheating, but here's the approach I would take:
def string_bump(s):
letter_list = "abcdefghijklmnopqrstuvwxyza" #note the extra 'a' at the end
old_positions = []; new_positions = []
for character in s:
old_positions.append(letter_list.find(character))
for pos in old_positions:
new_positions.append(pos+1)
new_string = ""
for pos in new_positions:
new_string += letter_list[pos]
return new_string
for s in ["abc", "bcd", "xyz"]:
print("before:", s, "after:", string_bump(s))
prints:
before: abc after: bcd
before: bcd after: cde
before: xyz after: yza
Basically, I scan the string to convert the characters to positions in the alphabet string; add 1 to each position; and rebuild the string from those positions. The "cheat" is adding an extra 'a' so a position-25 (counting from 0) 'z' translates to that extra position-26 'a'.
If that offends you, you could leave off the extra 'a' and instead just take another pass at the list of positions and when you see "26" (which would be past the end of the letter_list without the 'a'), knock it down to zero.
This is just a proof-of-concept for your example; to support an arbitrary shift, you'd extend the letter_list out for the full alphabet, and use modulo on the input (e.g. n = n%26) to ensure the input stayed in range.
Also, I would actually use list expressions in place of the for loops, but you may not have encountered those yet, so I used the more explicit for loops instead above.
Let's break it down so that individual steps with named variables make it clear what you're dealing with:
asciiValue = ord(string[characterIndex])
alphabetIndex = asciiValue - ord('a')
alphabetIndex = (alphabetIndex + n) % 26
asciiValue = alphabetIndex + ord('a')
string[characterIndex] = chr(asciiValue)
Note that the above assumes that your input string is composed only of lowercase ASCII letters. For uppercase characters, you'd need to subtract (and re-add) ord('A') instead.
Integrating it into your existing code:
def shift_letter(letter, n):
asciiValue = ord(letter)
alphabetIndex = asciiValue - ord('a')
alphabetIndex = (alphabetIndex + n) % 26
asciiValue = alphabetIndex + ord('a')
return chr(asciiValue)
string = list( input( "Insert a string, it will be codified: " ) )
n = int( input( "Insert an integer, each string's character will be increased by that number: " ) )
for characterIndex in range( len( string ) ):
string[characterIndex] = shift_letter(string[characterIndex], n)
print( ''.join( string ) )
I thought since we needed to encrypt only the alphabets I created a list of alphabets and looped them as below. This way when you input alphabets only alphabets shifted by the given number comes out even if the shift is large.
print("welcome to encryptor!!")
print("Do you want to encrypt or decrypt")
s = int(input("Press 1 for encrypt and 2 for decrypt: "))
alpha = list("abcdefghijklmnopqrstuvwxyz")
print(alpha)
def encrypt():
string = list(input("Enter your string without space to encrypt: "))
print(string)
string1 = []
n = int(input("Enter the value key for caesar crypt: "))
for characterIndex in range(len(string)):
for i in range(len(alpha)):
if string[characterIndex] == alpha[i]:
if((i + n)+1) >= 26:
string1.append(alpha[(i+n) % 26])
else:
string1.append(alpha[i+n])
else:
continue
print(''.join(string1))
This code does produce the desired ouput..
Here is the modified answer from the comments:
c = chr((ord(a) - 97) % 25 + 97)
def caesar_cipher(offset, string):
words = string.replace(" ", " ")
cipher_chars = "abcdefghijklmnopqrstuvwxyz"
word_i = 0
while word_i < len(words):
word = words[word_i]
letter_i = 0
while letter_i < len(word):
char_i = ord(word[letter_i]) - ord("c")
new_char_i = (char_i + offset) % 26
value = chr(new_char_i + ord("c"))
letter_i += 1
word_i += 1
return words.join(value)
print caesar_cipher(3, "abc")
Hey everyone, for some reason my ceasar cipher is only printing the last letter in my string, when I want it to cipher the whole string, for example, if i print an offset of 3 with string "abc" it should print def, but instead is just printing the f. Any help is greatly appreciated!
value is overwritten in the loop. You want to create a list passed to join (ATM you're joining only 1 character):
value = []
then
value.append(chr(new_char_i + ord("c")))
the join statement is also wrong: just do:
return "".join(value)
Note that there are other issues in your code. It seems to intent to process several words, but it doesn't, so a lot of loops don't loop (there's no list of words, it's just a word), so what you are doing could be summarized to (using a simple list comprehension):
def caesar_cipher(offset, string):
return "".join([chr((ord(letter) - ord("c") + offset) % 26 + ord("c")) for letter in string])
and for a sentence:
print(" ".join([caesar_cipher(3, w) for w in "a full sentence".split()]))
As a nice commenter noted, using c as start letter is not correct since it trashes sentences containing the 3 last letters. There's no reason not to start by a (the result are the same for the rest of the letters):
def caesar_cipher(offset, string):
return "".join([chr((ord(letter) - ord("a") + offset) % 26 + ord("a")) for letter in string])
Aside: a quick similar algorithm is rot13. Not really a cipher but it's natively supported:
import codecs
print(codecs.encode("a full sentence","rot13"))
(apply on the encoded string to decode it)
I have been playing with Python and came across a task from MIT, which is to create coded message (Julius Cesar code where for example you change ABCD letters in message to CDEF). This is what I came up with:
Phrase = input('Type message to encrypt: ')
shiftValue = int(input('Enter shift value: '))
listPhrase = list(Phrase)
listLenght = len(listPhrase)
ascii = []
for ch in listPhrase:
ascii.append(ord(ch))
print (ascii)
asciiCoded = []
for i in ascii:
asciiCoded.append(i+shiftValue)
print (asciiCoded)
phraseCoded = []
for i in asciiCoded:
phraseCoded.append(chr(i))
print (phraseCoded)
stringCoded = ''.join(phraseCoded)
print (stringCoded)
The code works but I have to implement not shifting the ascii value of spaces and special signs in message.
So my idea is to select values in list in range of range(65,90) and range(97,122) and change them while I do not change any others. But how do I do that?
If you want to use that gigantic code :) to do something as simple as that, then you keep a check like so:
asciiCoded = []
for i in ascii:
if 65 <= i <= 90 or 97 <= i <= 122: # only letters get changed
asciiCoded.append(i+shiftValue)
else:
asciiCoded.append(i)
But you know what, python can do the whole of that in a single line, using list comprehension. Watch this:
Phrase = input('Type message to encrypt: ')
shiftValue = int(input('Enter shift value: '))
# encoding to cypher, in single line
stringCoded = ''.join(chr(ord(c)+shiftValue) if c.isalpha() else c for c in Phrase)
print(stringCoded)
A little explanation: the list comprehension boils down to this for loop, which is easier to comprehend. Caught something? :)
temp_list = []
for c in Phrase:
if c.isalpha():
# shift if the c is alphabet
temp_list.append(chr(ord(c)+shiftValue))
else:
# no shift if c is no alphabet
temp_list.append(c)
# join the list to form a string
stringCoded = ''.join(temp_list)
Much easier it is to use the maketrans method from the string module:
>>import string
>>
>>caesar = string.maketrans('ABCD', 'CDEF')
>>
>>s = 'CAD BA'
>>
>>print s
>>print s.translate(caesar)
CAD BA
ECF DC
EDIT: This was for Python 2.7
With 3.5 just do
caesar = str.maketrans('ABCD', 'CDEF')
And an easy function to return a mapping.
>>> def encrypt(shift):
... alphabet = string.ascii_uppercase
... move = (len(alphabet) + shift) % len(alphabet)
... map_to = alphabet[move:] + alphabet[:move]
... return str.maketrans(alphabet, map_to)
>>> "ABC".translate(encrypt(4))
'EFG'
This function uses modulo addition to construct the encrypted caesar string.
asciiCoded = []
final_ascii = ""
for i in ascii:
final_ascii = i+shiftValue #add shiftValue to ascii value of character
if final_ascii in range(65,91) or final_ascii in range(97,123): #Condition to skip the special characters
asciiCoded.append(final_ascii)
else:
asciiCoded.append(i)
print (asciiCoded)
what i want to do is take a string and for each character make the ordinal value 1 more from the value it has.
myinput=input("Message : ")
mylist =list(myinput) #convert to list in order to take each character
for character in mylist:
mylist[character]+=ord(mylist[character])+1
print(character)
The problem is with the "ord(mylist[character])+1"
Thank you!
Probably you are looking for the next:
>>> m = raw_input('Message:')
Message:asdf
>>> ''.join(chr(ord(c) + 1) for c in m)
'bteg'
Notes:
use raw_input when you need to get string input from a user;
ord convert character to integer, chr - vise versa;
... for c in m syntax is a generator expression. It is also used for list comprehension.
Three problems here. First, you're mixing up list indices and list elements. Second, you didn't convert back to a character (I'm assuming you want characters, not numbers). Third, you're adding to the existing value.
One way:
for i range(len(mylist)):
mylist[i] = chr(ord(mylist[i])+1)
Another way:
for i, character in enumerate(mylist):
mylist[i] = chr(ord(character)+1)
Instead of
for character in mylist:
mylist[character]+=ord(mylist[character])+1
(where character is a list index and therefore invalid), you probably want:
mylist = [ord(character) + 1 for character in mylist]
Or a Counter.
You can do like this
def ordsum(astring, tablesize):
sum = 0
for num in range(len(astring)):
sum = sum + ord(astring[num])
return sum
myinput = input() # use raw_input() in Python 2
myinput = map(lambda ch: chr(ord(ch) + 1), myinput)
# or list comp.
myinput = [chr(ord(ch) + 1) for ch in myinput]
You can iterate directly over a string, you do not have to make it a list first. If your end goal is to have a new string, you can do this:
myinput=input("Message : ")
result = []
for character in myinput:
result.append( chr( ord( character ) + 1 )
mynewstring = ' '.join(result)