【问题标题】:Implementing the Caesar cipher algorithm in Python在 Python 中实现凯撒密码算法
【发布时间】:2017-06-20 04:14:45
【问题描述】:

所以我正在尝试编写一个代码,它将单词中的每个字母向后移动字母表中的多个字母(在结尾处环绕)。例如,如果我想移位 2 并输入 CBE,我应该得到 AZC。或约翰进入 HMFL。我有一个代码只能用于一个字母,我想知道是否有办法为 python 做一个嵌套的 for 循环(有效?)

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

【问题讨论】:

  • 这对 1 个字母有什么作用?
  • 这仅适用于一个字母,因为您的范围从 26 开始,因此在字母表中只匹配一次,并且条件只会匹配一次。
  • Scott 如果我调用 print move("B", 2) 我会按预期得到 Z。 :)
  • 您在 Caesar 密码算法中实现的内容。网上有很多解释。见:rosettacode.org/wiki/Caesar_cipher#Python

标签: python algorithm for-loop iteration


【解决方案1】:

您可以使用:chr() 为您提供 ascii 编号的字符,ord() 为您提供匹配字符的 ascii 编号。

这是一个古老的 Vigenere 项目:

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

【讨论】:

    【解决方案2】:

    你可以这样开始

    def move(word, shift):
        alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
        return "".join([alphabet[alphabet.find(i)-shift] for i in word])
    

    基本上,这个列表推导构造了一个单个字母的列表。然后,通过 .find 方法找到字母表中字母的索引。 (index - shift) 是从字母表中提取的所需新索引。结果列表再次加入并返回。

    请注意,它显然不适用于小写输入字符串(如果您希望使用 str.upper 方法)。实际上,这个词应该只由字母表中的字母组成。对于句子,该方法需要以不同的方式处理空格。

    【讨论】:

    • 这不起作用,比如'B'和 4 的转变。用 rfind 替换 find,我认为你有解决方案的精髓。
    • @Prune 你能解释一下为什么吗?在这种情况下,预期的输出是“X”吗?
    • 我暂时忘记了右端索引;它适用于小于 52 的班次。但是,对于超出双字母范围的任何内容,它都会出现索引错误。如果您依赖的值
    【解决方案3】:

    不要以那个的方式找到字母表中的字母——通过索引操作找到它。让 char 成为有问题的字母:

    alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
    ...
    char_pos = alphabet.index(char)
    new_pos = (char_pos - shift) % len(alphabet)
    new_char = alphabet[new_pos]
    

    一旦理解了这一点,就可以将这三行合并为一行。

    现在,让它作用于整个单词...

    new_word = ""
    for char in word:
        # insert the above logic
        new_word += new_char
    

    你能把所有这些部分放在一起吗?

    您仍然需要检查一下 char 是不是一个字母。另外,如果您有兴趣,可以为所有翻译的字符构建一个列表理解,并应用 ''.join() 来获取您的新单词。

    比如……

    如果字母在字母表中(如果 char 在字母表中),则移动给定的距离并获取新字母,如果需要,在末尾环绕 (% 26)。如果不是大写字母,则使用原始字符。

    从所有这些翻译中列出一个列表,然后将它们连接成一个字符串。返回那个字符串。

    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)
    

    输出:

    HAL
    Ghe 1812 BIREGHER is PBBY!
    

    【讨论】:

    • 哇,这比我想出的要优雅得多。
    【解决方案4】:
    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
    

    请注意,这仅适用于小写单词。一旦你开始混合大写和小写字母,你就需要开始检查它们。但这是一个开始。我放弃了您的嵌套循环想法,因为您应该尽可能避免使用这些想法。

    【讨论】: