ROT(n)编码器和解码器,但解码器不起作用

时间:2019-07-23 11:28:23

标签: python python-3.x encryption rot13

我知道有很多方法可以编写ROT(n)函数。 但是我不想有一些带字符的表。

因此,我尝试编写一个带有解码器的简单ROT(n)作为练习项目。 编码功能工作正常。但是解码器不断将“ a”更改为“ z”。

有人可以告诉我我在做什么错吗?

下面的(Python3)代码将所有内容更改为小写,而忽略了任何特殊字符。

import random
import string

shift = random.randint(1, 20)


# Encoder:
def encode(string):
    coded_string = []
    string = string.lower()
    for c in string:
        if ord(c) >= 97 and ord(c) <= 122:
            c = (ord(c) + shift) % 122
            if c <= 97:
                c += 97
            coded_string.append(chr(c))
            continue
        coded_string.append(c)
    return ''.join(coded_string)


# Decoder:
def decode(string):
    decoded_string = []
    for c in string:
        if ord(c) >= 97 and ord(c) <= 122:
            if ord(c) - shift <= 97:
                c = (ord(c) % 97) + (122 - shift)
                decoded_string.append(chr(c))
                continue
            c = ord(c) - shift
            decoded_string.append(chr(c))
            continue
        decoded_string.append(c)
    return ''.join(decoded_string)


# Test Function:
def tryout(text):
    test = decode(encode(text))
    try:
        assert test == text, 'Iznogoedh!'
    except AssertionError as AE:
        print(AE, '\t', test)
    else:
        print('Yes, good:', '\t', test)


# Random text generator:
def genRandomWord(n):
    random_word = ''
    for i in range(n):
        random_word += random.choice(string.ascii_lowercase)
    return random_word


# Some tests:
print(f'Shift: {shift}')
tryout('pokemon')
tryout("chip 'n dale rescue rangers")
tryout('Ziggy the Vulture or Zurg')
tryout('Fine # (*day%, to* code@ in Pyth0n3!')
tryout(genRandomWord(10))
tryout(genRandomWord(20))

示例输出:

Shift: 7
Yes, good:   pokemon
Iznogoedh!   chip 'n dzle rescue rzngers
Iznogoedh!   ziggy the vulture or zurg
Iznogoedh!   fine # (*dzy%, to* code@ in pyth0n3!
Yes, good:   qrwmfyogjg
Yes, good:   ihrcuvzyznlvghrtnuno

但是,我希望忽略随机字符串测试:

Shift: 7
Yes, good:   pokemon
Yes, good:   chip 'n dale rescue rangers
Yes, good:   ziggy the vulture or zurg
Yes, good:   fine # (*day%, to* code@ in pyth0n3!

1 个答案:

答案 0 :(得分:1)

首先,您的tryout()测试函数忘记小写输入,因此对于您实际通过的 Ziggy 示例来说,它失败了;正确的测试是:

# Test Function:
def tryout(text):
    test = decode(encode(text))
    try:
        assert test == text.lower(), 'Iznogoedh!'
    except AssertionError as AE:
        print(AE, '\t', test)
    else:
        print('Yes, good:', '\t', test)

错误出现在您的 decode 函数中;对于7的偏移,您可以看到a-> h的编码字母不能正确地映射回去,而i(来自b)可以正常工作:

>>> decode('h')
'z'
>>> decode('i')
'b'

但是,错误进一步发展;前7个字母中的每个字母均被错误翻译; g映射到yf映射到x,依此类推。如果使用较低的移位,则很容易看到:

>>> for encoded in 'abcd': print(decode(encoded), end=' ')
... else: print()
...
w x y z

这些应该已映射回xyza。因此,这是一个 off-by-one 错误,它在您的测试中:

if ord(c) - shift <= 97:

shift为3,而cd时,ord(c) - shift等于97,并且不应该调整。将<=更改为<

if ord(c) - shift < 97:

因此,固定的decode()函数将变为:

def decode(string):
    decoded_string = []
    for c in string:
        if ord(c) >= 97 and ord(c) <= 122:
            if ord(c) - shift < 97:
                c = (ord(c) % 97) + (122 - shift)
                decoded_string.append(chr(c))
                continue
            c = ord(c) - shift
            decoded_string.append(chr(c))
            continue
        decoded_string.append(c)
    return ''.join(decoded_string)

您可能想在此处了解% modulo 运算符,该运算符可以帮助“环绕”值以适合某个范围,例如字母{{ 1}}至a

如果您采用ASCII码点,请减去97,然后使用调整后的值(减号或加号移位,具体取决于编码或解码),然后将结果值用z换行,您总会得出'另一边”,可以将结果添加回97:

% 26

另一个技巧是通过将您的输入编码为UTF-8来使用bytes object>>> ord('a') - 97 # a is the 'zeroth' letter in the alphabet, z is the 25th 0 >>> ord('a') - 97 - shift # shifted by 3 puts it outside the 0 - 25 range -3 >>> (ord('a') - 97 - shift) % 26 # modulo 26 puts it back in the range, from the end 23 >>> chr((ord('a') - 97 - shift) % 26 + 97) # add 97 back on to go back to the decoded letter 'x' 对象是整数序列,可以说bytes函数已经对其进行了处理。只需循环并将移位应用于正确范围内的字节,然后将这些整数附加到列表中即可。然后,您可以从列表中创建一个新的ord()对象并解码回字符串:

bytes

只需将移位作为正值或负值传入,上述函数就可以同时用于 encoding decoding

def shift_by_n(n, value):
    as_bytes = value.lower().encode('utf8')
    encoded = []
    for v in as_bytes:
        if 97 <= v <= 122:
            v = ((v - 97 + n) % 26) + 97
        encoded.append(v)
    return bytes(encoded).decode('utf8')

最后,您可以使用str.translate() function而不是测试每个字母,给定翻译表,它可以为您替换所有文字。您可以使用str.maketrans() static method轻松构建ROT(n)转换表。编码只是简单地映射到相同字母的字母,但是从一开始就带有def encode(string): return shift_by_n(shift, string) def decode(string): return shift_by_n(-shift, string) 个字符并将其添加到末尾:

shift

解码使用相同的alphabet = 'abcdefghijklmnopqrstuvwxyz' def encode(string): # take all letters except the first 'shift' characters, and # add those letters to the end instead rotated = alphabet[shift:] + alphabet[:shift] translate_map = str.maketrans(alphabet, rotated) return string.lower().translate(translate_map) 字符串,但是交换rotated的参数顺序:

str.maketrans()

使以上功能也可以使用大写字母,只需要将def decode(string): # take all letters except the first 'shift' characters, and # add those letters to the end instead rotated = alphabet[shift:] + alphabet[:shift] translate_map = str.maketrans(rotated, alphabet) return string.translate(translate_map) alphabet.upper()的结果连接到rotated.upper()alphabet分别调用rotated时(并删除str.maketrans()中的.lower()调用)。我将把它留给读者来实现。