我很高兴解决来自 Pythonchallenge 网站的谜语
当我偶然发现一种奇怪的行为时:
使用此输入: *g fmnc wms bgblr rpylqjyrc gr zw fylb。 rfyrq ufyr amknsrcpq ypc dmp。 bmgle gr gl zw fylb gq glcddgagclr ylb rfyr'q ufw rfgq rcvr gq qm jmle。 sqgle qrpgle.kyicrpylq() gq pcamkkclbcb。 lmu ynnjw ml rfc spj。
我们应该能够得到以下输出: *“我希望你没有手工翻译它。这就是计算机的用途。手工翻译效率低下,这就是为什么此文本如此长。建议使用 string.maketrans()。现在应用于 url。” *
相反,当我们用简单的 ROT2 脚本破译它时,我们得到的是: 我希望你没有 tr{nsl{te it |y h{nd0 th{ts wh{t 计算机 {re for0 这样做 |y h{nd 效率低下 {nd th{t)s 为什么这个文本这么长 0 使用 string0m{ ketr{ns+ 现在推荐0 {应用在url0*
我引用的 ROT2 脚本如下如下:
user_input = input().split(' ')
newletter_int = 0
new_output = []
for word in user_input:
newletter_int = 0
newstr = ''
for letter in word:
newletter = ord(letter) + 2
newstr += chr(newletter)
new_output.append(newstr)
print(" ".join(new_output))
这当然会发生,因为字母“y”的序号为121,当我们将2加到121时,我们得到序号为123的字符“{”。但为什么 Python maketrans 会产生正确的字符呢?
请注意,我已经用 maketrans 解决了这个任务,我正在寻找的并不是这个谜语的解决方案,因为我自己已经找到了答案。我正在寻找一个简单的解释,这两种方法之间有什么区别。另外,请不要参考链接解决方案的页面,因为我不是在寻找它们,而是为了解释上面的脚本和 string.maketrans() 方法之间的功能差异以及答案问题是为什么这是解决谜题的推荐方法。
我假设在使用
str.maketrans
时,您专门在字母之间创建了一个 mapping 表。
这正是要点:您指定字符之间的一对一映射,因此您可以保证这些转换将按照您指定的方式发生。
现在让我们看一下您的脚本:
ord
,它返回一个整数,表示您传递给它的字符的 Unicode 代码点。
这本质上意味着我们应该对字符编码有一些基本知识。
对于这个问题,您可以忽略 Unicode 代码点,因为我们正在处理可以使用 ASCII 编码的字符(设计 Unicode 的聪明人确保前 256 个代码点是相同的)。
为了理解 ASCII 表上发生的事情,你最好的朋友是:现在让我们看看 ROT2 实现中最重要的一行:
newletter = ord(letter) + 2
。
看看上表,应该很清楚为什么y
会转化为{
或者为什么.
会变成0
。
正因为如此,我们需要在实施方面更加聪明一些;具体来说,我们需要仔细研究跨越该界限的场景。
规避此问题的常见方法是使用类似 (ord(letter) - 97 + 2) % 26 + 97
的内容。
我会让你自己弄清楚为什么它有效。
我看到你正在使用
str.split
,这样你就可以避免变换空间。
不幸的是,这还不够,因为您要转换的字符串包含其他非字母,例如'
或.
。
我建议你看一下 string
模块提供的常量。
至于为什么这可能是解决这个谜题的推荐方法,我猜这正是因为使用加法手动转换字符所涉及的所有努力。 正如我试图用我的答案来说明的那样,指定 1 对 1 字符映射并将其直接应用于字符串要简单得多。
但是为什么 Python maketrans 会产生正确的字符呢?
makeTrans
只是提供了一种迭代字符的方法;最终传递的映射仍然取决于提供的字典/输入参数。
如果传递了一个映射,将 Y 映射到 A 进行加密,将 A 映射到 Y 进行解密,那么
makeTrans
显然可以正常工作,因为字典已经包含您忘记应用的环绕。
所以你定义的函数映射:
ABCDEFGHIJKLMNOPQRSTUVWXYZ
CDEFGHIJKLMNOPQRSTUVWXYZ{|
虽然它应该执行以下加密映射:
ABCDEFGHIJKLMNOPQRSTUVWXYZ
CDEFGHIJKLMNOPQRSTUVWXYZAB
和
makeTrans
只是接收正确的映射(如果不指定第三个参数,则将其他字符保留原样)。
请注意,执行环绕的好方法是将字母转换为提供的字母表中的索引,然后使用模运算符执行环绕:
%
,而不是直接对 ASCII 值进行操作(因此 A
映射到 0 而不是十进制 97)。