Python中的凯撒密码函数

23
我正在尝试在Python中创建一个简单的凯撒密码函数,根据用户输入移动字母并在最后创建一个新字符串。唯一的问题是最终的密文只显示最后一个移位字符,而不是包含所有移位字符的完整字符串。
以下是我的代码:
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)

Python 3 凯撒密码实现 - jfs
原始代码中的问题是一个基本的逻辑错误,可以被视为拼写错误,因为仔细检查时错误的代码毫无意义 - 很明显,在 cipherText = "" 之后立即 执行 cipherText += finalLetter 会产生一个只包含当前 finalLetter 的结果。如果人们将问题解释为“如何实现密码?”那么应该删除代码,并重新聚焦问题,提出具体要求。 - Karl Knechtel
27个回答

61

我知道这个回答并不能真正回答你的问题,但我认为它仍然是有用的。这里有一种使用字符串方法实现凯撒密码的替代方式:

def caesar(plaintext, shift):
    alphabet = string.ascii_lowercase
    shifted_alphabet = alphabet[shift:] + alphabet[:shift]
    table = string.maketrans(alphabet, shifted_alphabet)
    return plaintext.translate(table)

实际上,由于字符串方法是用C实现的,因此我们将在此版本中看到性能提高。这就是我认为实现此操作的“Pythonic”方式。


19
上面的解决方案适用于Python 2,但在Python 3中,请使用str.maketrans()而不是string.maketrans() - cclauss
2
这是针对多个字母表(小写、大写、数字等)的改进代码版本:https://dev59.com/bGox5IYBdhLWcg3w6on0#54590077 - Georgy

21

你需要在for循环开始之前移动cipherText = ""。这样做是因为你每次循环都会重置它。

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

11

这是对 @amillerrhodes 的回答 中的代码进行了改进,它适用于不仅限于小写字母的不同字符集:

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)

使用示例:

>>> import string
>>> alphabets = (string.ascii_lowercase, string.ascii_uppercase, string.digits)
>>> caesar('Abc-xyZ.012:789?жñç', step=4, alphabets=alphabets)
'Efg-bcD.456:123?жñç'

参考文献:
str.maketrans 文档.
str.translate 文档.
string 库文档


5

使用一些ASCII编码技巧:

# See http://ascii.cl/
upper = {ascii:chr(ascii) for ascii in range(65,91)}
lower = {ascii:chr(ascii) for ascii in range(97,123)}
digit = {ascii:chr(ascii) for ascii in range(48,58)}


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)

这里的 s 是加密字符串,k 是整数位移量。 - Arnb

3
电池已包含在内。
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!"

https://docs.python.org/2/library/codecs.html#python-specific-encodings

Python 3更新

精细文档中指出:

现在 rot_13 编码器提供了一种文本转换方式:字符串到字符串的映射。它不被 str.encode() 支持(该方法只产生字节输出)。

换句话说,您需要从 codecs 模块导入 encode 并将要编码的字符串作为其第一个参数使用。

from codecs import decode
...
    print(encode(phrase, 'rot13'))

rot13密码是凯撒密码的一种特殊情况,其移位为13,因此只有当用户选择了13个移位时才能使用。 - Nick McCurdy

3
问题在于每次循环迭代时,您将cipherText设置为空字符串,在以下行:
```python cipherText = '' ```
cipherText = ""

必须在循环之前移动。


3
如其他人所指出的,您在for循环的迭代中重置了cipherText。将cipherText放在for循环之前可以解决您的问题。
此外,还有一种使用Python标准库解决此问题的替代方法。 Python标准库定义了一个函数maketrans()和一个在字符串上操作的方法translate()。
函数maketrans()创建可以与translate方法一起使用以更有效地将一组字符更改为另一组字符的转换表。(摘自 Python标准库范例)。
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)

2
这里有一种更加实用的方法: (如果您使用Shift I进行编码,则使用-I进行解码)
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 
        ])

1
>>> 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!'

1
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()

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接