python - ROT(n) 编码器和解码器,但解码器不工作

标签 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!

最佳答案

首先,您的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)

错误出现在您的解码函数中;对于 7 的移位,您可以看到 a 的编码字母-> h无法正确映射回来,而 i (来自 b ),确实有效:

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

然而,错误更进一步;前 7 个字母均被误译; g映射到y , f映射到x等。如果您使用较低的类次,则很容易看到:

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

那些应该已映射回x , y , za 。所以这是一个相差一错误,它在您的测试中:

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)

您可能想了解%此处的取模运算符,可以帮助“环绕”值以适应某个范围,例如字母 a 的值范围通过z .

如果采用 ASCII 代码点,减去 97,然后使用调整后的值(减或加移位,具体取决于编码或解码),然后用 % 26 包装结果值,您总是会出现在“另一边”,并且可以将结果加回到 97:

>>> 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 object ,将您的输入编码为 UTF-8。 bytes对象是整数序列,已由ord()处理。函数,可以这么说。只需循环并将移位应用于正确范围内的字节,并将这些整数附加到列表中。然后您可以创建一个新的 bytes列表中的对象并解码回字符串:

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')

上述函数可以同时用于编码解码,只需将移位作为正值或负值传递即可:

def encode(string):
    return shift_by_n(shift, string)

def decode(string):
    return shift_by_n(-shift, string)

最后,您可以使用 str.translate() function,而不是测试每个字母。给定一个翻译表,它可以为您进行所有替换。您可以使用 str.maketrans() static method 轻松构建 ROT(n) 转换表。 。编码只是将字母表映射到相同的字母表,但带有 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()结果为alphabetrotated ,分别调用 str.maketrans() 时(并删除 .lower() 中的 encode() 调用)。我将把它留给读者来实现。

关于python - ROT(n) 编码器和解码器,但解码器不工作,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57163173/

相关文章:

python - 类型错误 : 'str' does not support the buffer interface - python

php - PHP中有bcompiler的反编译器吗?

MySQL 加密

python - argparse。 nargs + 自定义类型时不起作用

Python Pickle 文件权限

python - python中的继承问题

python - 线条不显示在条形图上

python - 使用索引张量和 tf.gather 对张量进行切片

Python 3.6.3 urlopen 从存储在远程服务器上的 html 文件的 URI 中删除服务器名称

c - 加密文件中没有填充的 AES 解密