1

我知道有很多方法可以编写 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!
4

1 回答 1

2

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

那些应该映射回x,y和. 所以这是一个错误的错误,它在你的测试中:za

if ord(c) - shift <= 97:

什么时候shift是 3,并且cdord(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)

您可能想在此处了解% 运算符,它可以帮助“环绕”值以适应范围,例如字母的值范围az.

如果你取 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'

另一个“技巧”是通过将输入编码为 UTF-8来使用bytesobject 。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()最后,您可以使用给定翻译表的功能,而不是测试每个字母,该功能会为您进行所有替换。str.maketrans()您可以使用静态方法轻松构建 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()androtated.upper()结果分别连接到alphabetand (并删除 中的调用)。我将把它留给读者来实现。rotatedstr.maketrans().lower()encode()

于 2019-07-23T11:45:47.057 回答