I'm trying to create a simple Caesar Cipher function in Python that shifts letters based on input from the user and creates a final, new string at the end. The only problem is that the final cipher text shows only the last shifted character, not an entire string with all the shifted characters. Here's my code:
plainText = raw_input("What is your plaintext? ") shift = int(raw_input("What is your shift? ")) def caesar(plainText, shift): for ch in plainText: if ch.isalpha(): stayInAlphabet = ord(ch) + shift if stayInAlphabet > ord('z'): stayInAlphabet -= 26 finalLetter = chr(stayInAlphabet) cipherText = "" cipherText += finalLetter print "Your ciphertext is: ", cipherText return cipherText caesar(plainText, shift)
411k 201 201 gold badges 1k 1k silver badges 1.7k 1.7k bronze badges
asked Jan 16, 2012 at 22:05
user1063450 user1063450
Commented Oct 14, 2014 at 23:00
The problem in the original code is an elementary logic error that can be considered a typo because the erroneous code makes no sense when carefully examined - obviously doing cipherText += finalLetter immediately after cipherText = "" produces a result that only has the current finalLetter in it. If people interpreted the question as "how do I implement a cipher?" then the code should be removed and the question should be refocused with specific requirements.
Commented Jul 28, 2023 at 5:32I realize that this answer doesn't really answer your question, but I think it's helpful anyway. Here's an alternative way to implementing the caesar cipher with string methods:
def caesar(plaintext, shift): alphabet = string.ascii_lowercase shifted_alphabet = alphabet[shift:] + alphabet[:shift] table = string.maketrans(alphabet, shifted_alphabet) return plaintext.translate(table)
In fact, since string methods are implemented in C, we will see an increase in performance with this version. This is what I would consider the 'pythonic' way of doing this.
answered Jan 17, 2012 at 13:35 amillerrhodes amillerrhodes 2,692 1 1 gold badge 18 18 silver badges 19 19 bronze badgesThe solution above works in Python 2 but in Python 3 use str.maketrans() instead of string.maketrans() .
Commented Jan 17, 2017 at 5:55Improved version of this code for the case of several alphabets (lowercase, uppercase, digits, etc.): stackoverflow.com/a/54590077/7851470
Commented Feb 8, 2019 at 10:12You need to move cipherText = "" before the start of the for loop. You're resetting it each time through the loop.
def caesar(plainText, shift): cipherText = "" for ch in plainText: if ch.isalpha(): stayInAlphabet = ord(ch) + shift if stayInAlphabet > ord('z'): stayInAlphabet -= 26 finalLetter = chr(stayInAlphabet) cipherText += finalLetter print "Your ciphertext is: ", cipherText return cipherText
answered Jan 16, 2012 at 22:07
27.2k 14 14 gold badges 89 89 silver badges 119 119 bronze badges
This is an improved version of the code in the answer of @amillerrhodes that works with different alphabets, not just lowercase:
def caesar(text, step, alphabets): def shift(alphabet): return alphabet[step:] + alphabet[:step] shifted_alphabets = tuple(map(shift, alphabets)) joined_aphabets = ''.join(alphabets) joined_shifted_alphabets = ''.join(shifted_alphabets) table = str.maketrans(joined_aphabets, joined_shifted_alphabets) return text.translate(table)
Example of usage:
>>> import string >>> alphabets = (string.ascii_lowercase, string.ascii_uppercase, string.digits) >>> caesar('Abc-xyZ.012:789?жñç', step=4, alphabets=alphabets) 'Efg-bcD.456:123?жñç'
answered Feb 8, 2019 at 10:10
13.5k 7 7 gold badges 67 67 silver badges 77 77 bronze badges
Using some ascii number tricks:
# See http://ascii.cl/ upper = lower = digit = def ceasar(s, k): for c in s: o = ord(c) # Do not change symbols and digits if (o not in upper and o not in lower) or o in digit: yield o else: # If it's in the upper case and # that the rotation is within the uppercase if o in upper and o + k % 26 in upper: yield o + k % 26 # If it's in the lower case and # that the rotation is within the lowercase elif o in lower and o + k % 26 in lower: yield o + k % 26 # Otherwise move back 26 spaces after rotation. else: # alphabet. yield o + k % 26 -26 x = (''.join(map(chr, ceasar(s, k)))) print (x)
answered Jan 3, 2016 at 16:34
121k 113 113 gold badges 485 485 silver badges 784 784 bronze badges
Here s is the encrypted string and k is the shift in integer.
Commented Jan 20, 2020 at 8:58
The problem is that you set cipherText to empty string at every cycle iteration, the line
cipherText = ""
must be moved before the loop.
answered Jan 16, 2012 at 22:09 Dmitry Polyanitsa Dmitry Polyanitsa 1,083 9 9 silver badges 18 18 bronze badgesAs pointed by others, you were resetting the cipherText in the iteration of the for loop. Placing cipherText before the start of the for loop will solve your problem.
Additionally, there is an alternate approach to solving this problem using Python's Standard library. The Python Standard Library defines a function maketrans() and a method translate that operates on strings.
The function maketrans() creates translation tables that can be used with the translate method to change one set of characters to another more efficiently. (Quoted from The Python Standard Library by Example).
import string def caesar(plaintext, shift): shift %= 26 # Values greater than 26 will wrap around alphabet_lower = string.ascii_lowercase alphabet_upper = string.ascii_uppercase shifted_alphabet_lower = alphabet_lower[shift:] + alphabet_lower[:shift] shifted_alphabet_upper = alphabet_upper[shift:] + alphabet_upper[:shift] alphabet = alphabet_lower + alphabet_upper shifted_alphabet = shifted_alphabet_lower + shifted_alphabet_upper table = string.maketrans(alphabet, shifted_alphabet) return plaintext.translate(table)
answered Apr 7, 2013 at 6:23
Wasif Hyder Wasif Hyder
1,349 2 2 gold badges 10 10 silver badges 17 17 bronze badges
while 1: phrase = raw_input("Could you please give me a phrase to encrypt?\n") if phrase == "" : break print "Here it is your phrase, encrypted:" print phrase.encode("rot_13") print "Have a nice afternoon!"
Python 3 update
[Now the rot_13 ] codec provides a text transform: a str to str mapping. It is not supported by str.encode() (which only produces bytes output).
Or, in other words, you have to import encode from the codecs module and use it with the string to be encoded as its first argument
from codecs import decode . print(encode(phrase, 'rot13'))
answered Mar 18, 2015 at 13:12
24.6k 9 9 gold badges 60 60 silver badges 94 94 bronze badges
The rot13 cipher is a specific case of the caesar cipher with shift 13, so this would only work if the user chose a shift of 13.
Commented Mar 8, 2023 at 15:03Here, a more functional way: (if you use shift i to encode, then use -i to decode)
def ceasar(story, shift): return ''.join([ # concentrate list to string (lambda c, is_upper: c.upper() if is_upper else c) # if original char is upper case than convert result to upper case too ( ("abcdefghijklmnopqrstuvwxyz"*2)[ord(char.lower()) - ord('a') + shift % 26], # rotate char, this is extra easy since Python accepts list indexs below 0 char.isupper() ) if char.isalpha() else char # if not in alphabet then don't change it for char in story ])
answered Oct 13, 2016 at 23:37
642 7 7 silver badges 20 20 bronze badges
plainText = raw_input("What is your plaintext? ") shift = int(raw_input("What is your shift? ")) def caesar(plainText, shift): for ch in plainText: if ch.isalpha(): stayInAlphabet = ord(ch) + shift if stayInAlphabet > ord('z'): stayInAlphabet -= 26 finalLetter = chr(stayInAlphabet) #####HERE YOU RESET CIPHERTEXT IN EACH ITERATION##### cipherText = "" cipherText += finalLetter print "Your ciphertext is: ", cipherText return cipherText caesar(plainText, shift)
As an else to if ch.isalpha() you can put finalLetter=ch .
You should remove the line: cipherText = ""
52.6k 17 17 gold badges 117 117 silver badges 142 142 bronze badges answered Jan 16, 2012 at 22:15 700 5 5 silver badges 16 16 bronze badgesAs @I82much said, you need to take cipherText = "" outside of your for loop. Place it at the beginning of the function. Also, your program has a bug which will cause it to generate encryption errors when you get capital letters as input. Try:
if ch.isalpha(): finalLetter = chr((ord(ch.lower()) - 97 + shift) % 26 + 97)
answered Jan 17, 2012 at 2:47
Joel Cornett Joel Cornett
24.7k 9 9 gold badges 68 68 silver badges 90 90 bronze badges
>>> def rotate(txt, key): . def cipher(i, low=range(97,123), upper=range(65,91)): . if i in low or i in upper: . s = 65 if i in upper else 97 . i = (i - s + key) % 26 + s . return chr(i) . return ''.join([cipher(ord(s)) for s in txt]) # test >>> rotate('abc', 2) 'cde' >>> rotate('xyz', 2) 'zab' >>> rotate('ab', 26) 'ab' >>> rotate('Hello, World!', 7) 'Olssv, Dvysk!'
answered Aug 21, 2017 at 20:11
20k 5 5 gold badges 78 78 silver badges 62 62 bronze badges
I have a hard time remember the char to int conversions so this could be optimized
def decryptCaesar(encrypted, shift): minRange = ord('a') decrypted = "" for char in encrypted: decrypted += chr(((ord(char) - minRange + shift) % 26) + minRange) return decrypted
answered Sep 29, 2017 at 20:51
Michael Luong Michael Luong
21 4 4 bronze badges
def encrypt(): plainText = input("What is your plaintext? ") shift = int(input("What is your shift? ")) cipherText = "" for ch in plainText: if ch.isalpha(): stayInAlphabet = ord(ch) + shift if stayInAlphabet > ord('z'): stayInAlphabet -= 26 finalLetter = chr(stayInAlphabet) cipherText += finalLetter print ("Your ciphertext is: ", cipherText,"with a shift of",shift) def decrypte(): encryption=input("enter in your encrypted code") encryption_shift=int(input("enter in your encryption shift")) cipherText1 = "" for c in encryption: if c.isalpha(): stayInAlphabet1 = ord(c) - encryption_shift if stayInAlphabet1 > ord('z'): stayInAlphabet1 += 26 finalLetter1 = chr(stayInAlphabet1) cipherText1 += finalLetter1 print ("Your ciphertext is: ", cipherText1,"with negative shift of",encryption_shift) from tkinter import * menu=Tk() menu.title("menu") menu.geometry("300x300") button1= Button(menu,text="encrypt",command=encrypt) button1.pack() button2= Button(menu,text="decrypt",command=decrypte) button2.pack() button3= Button(menu,text="exit",command=exit) button3.pack() menu.mainloop()
answered Oct 12, 2017 at 16:32
StudentPie StudentPie
11 1 1 bronze badge
message = 'The quick brown fox jumped over the lazy dog. 1234567890 !@#$%^&*()_+-' encrypted = ''.join(chr(ord(char)+3) for char in message) decrypted = ''.join(chr(ord(char)-3) for char in encrypted) print(encrypted) print(decrypted) # Wkh#txlfn#eurzq#ir|#grj1#456789:;
answered Jan 17, 2019 at 11:52
6,039 9 9 gold badges 63 63 silver badges 138 138 bronze badges
def encrypt(text,shift): ''' INPUT: text as a string and an integer for the shift value. OUTPUT: The shifted text after being run through the Caeser cipher. ''' # Create a placeholder list encrypted_text = list(range(len(text))) alphabet = string.ascii_lowercase # Create shifted alphabet first_half = alphabet[:shift] second_half = alphabet[shift:] shifted_alphabet = second_half+first_half for i,letter in enumerate(text.lower()): # Check for spaces or punctuation if letter in alphabet: # Find the original index position original_index = alphabet.index(letter) # Shifted letter new_letter = shifted_alphabet[original_index] encrypted_text[i] = new_letter # Punctuation or space else: encrypted_text[i] = letter return ''.join(encrypted_text)
135k 127 127 gold badges 298 298 silver badges 273 273 bronze badges
answered May 11, 2020 at 18:27
user13520241 user13520241
11 1 1 bronze badge
Code dumps without any explanation are rarely helpful. Stack Overflow is about learning, not providing snippets to blindly copy and paste. Please edit your question and explain how it works better than what the OP provided.
Commented May 11, 2020 at 19:20You should also relate to the code provided by the asker of the question. It looks like their code is very close to working (even if being a bit un-elegant in places), so relating to it would strengthen learning.
Commented May 11, 2020 at 19:50For example, decod string:
"uo jxuhu! jxyi yi qd unqcfbu ev q squiqh syfxuh. muhu oek qrbu je tusetu yj? y xefu ie! iudt cu q cuiiqwu rqsa myjx jxu iqcu evviuj!".
This message has an offset of 10.
import string alphabet = list(string.ascii_lowercase) print(alphabet, len(alphabet)) messege = "xuo jxuhu! jxyi yi qd unqcfbu ev q squiqh syfxuh. muhu oek qrbu je tusetu yj? y xefu ie! iudt cu q cuiiqwu rqsa myjx jxu iqcu evviuj!" messege_split = messege.split() print(messege_split) encrypted_messege = "" position = 0 for i in messege_split: for j in i: if ord(j) < 65: encrypted_messege += j else: for k in alphabet: if j == k: position = alphabet.index(k) if (position + 10) >= len(alphabet): encrypted_messege += alphabet[abs((position + 10) - len(alphabet))] else: encrypted_messege += alphabet[position + 10] encrypted_messege += " " print(encrypted_messege)
Decoded string:
"hey there! this is an example of a caesar cipher. were you able to decode it? i hope so! send me a message back with the same offset!"
answered Jul 9, 2020 at 16:40 Pobaranchuk Pobaranchuk 867 10 10 silver badges 13 13 bronze badgesUsing cyclic generator:
import string from itertools import cycle def caesarCipherEncryptor(s, key): def generate_letters(): yield from cycle(string.ascii_lowercase) def find_next(v, g, c): # Eat up characters until we arrive at the plaintext character while True: if v == next(g): break # Increment the plaintext character by the count using the generator try: for _ in range(c): item = next(g) return item except UnboundLocalError: return v return "".join([find_next(i, generate_letters(), key) for i in s]) # Outputs >>> caesarCipherEncryptor("xyz", 3) >>> 'abc'
4,514 1 1 gold badge 35 35 silver badges 78 78 bronze badges
answered Jan 18, 2021 at 16:35
Santhosh Balasa Santhosh Balasa
184 3 3 silver badges 11 11 bronze badges
This is a really nice approach which is configurable and avoids mod arithmetic. Very pythonic, thanks!
Commented Feb 21, 2021 at 22:57One downside, though, is that it doesn't allow character decrementing. To reverse the operation you would have to increment by the complement of the initial encryption.
Commented Feb 21, 2021 at 23:05from string import ascii_lowercase as alphabet class CaesarCypher: alpha_len = len(alphabet) min_guess_rate = 0.2
Encryption and decryption is a same stuff. when you want to decrypt for example with shift 10 that means that you can encrypt it with shift 26 - 10. In this case cycle will repeat at if you going to shift whole alphabet it will be the same. Also here i've proceed upper case and non chars
def __call__(self, text, offset, encrypt=True): if not encrypt: offset = self.alpha_len - offset result = [] for letter in text: if not letter.isalpha(): result.append(letter) continue letter_to_process = letter.lower() processed_letter = self._encrypt_letter(letter_to_process, offset) if letter.isupper(): processed_letter = processed_letter.upper() result.append(processed_letter) return ''.join(result)
all encryption goes here at most.
def _encrypt_letter(self, letter, offset=0): position = (alphabet.find(letter) + offset) % self.alpha_len return alphabet[position]
this part is for broot force and guess throug dictionary frequency.
@staticmethod def __how_many_do_i_know(text): clean_words = filter(lambda x: x.isalpha(), text.split()) clean_words = ['\'<>\''.format(x) for x in clean_words] cursor = conn.cursor() query = 'SELECT COUNT(*) FROM mydictionary WHERE word IN (<>)'.format(",".join(clean_words)) cursor.execute(query) response = cursor.fetchone()[0] return response / len(clean_words) def guess_encode(self, text): options = [self(text, offset, encrypt=False) for offset in range(self.alpha_len)] best_option = [self.__how_many_do_i_know(option) for option in options] best_key, guess_rate = max(enumerate(best_option), key=lambda x: x[-1]) guess_text = options[best_key] return best_key, guess_rate, guess_text