我知道有很多方法可以编写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!
答案 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
映射到y
,f
映射到x
,依此类推。如果使用较低的移位,则很容易看到:
>>> for encoded in 'abcd': print(decode(encoded), end=' ')
... else: print()
...
w x y z
这些应该已映射回x
,y
,z
和a
。因此,这是一个 off-by-one 错误,它在您的测试中:
if ord(c) - shift <= 97:
当shift
为3,而c
为d
时,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()
调用)。我将把它留给读者来实现。