XOR两个不同长度的字符串

时间:2019-03-05 17:11:02

标签: python-3.x

所以我试图对两个字符串进行异或运算,但是不确定当字符串长度不同时我是否正确执行了操作。 我使用的方法如下。

def xor_two_str(a,b):
xored = []
for i in range(max(len(a), len(b))):
    xored_value = ord(a[i%len(a)]) ^ ord(b[i%len(b)])
    xored.append(hex(xored_value)[2:])
return ''.join(xored)

我这样得到输出。

abc XOR abc: 000
abc XOR ab: 002
ab XOR abc: 5a
space XOR space: 0

我知道有什么问题,我最终会希望将十六进制值转换为ascii,因此担心基础错误。任何帮助将不胜感激。

1 个答案:

答案 0 :(得分:1)

您的代码看起来基本上是正确的(假设目标是通过循环回到开头来重复使用较短的输入),但是您的输出存在一个小问题:每个字符的宽度不是固定的,因此您可以从两个字符中获得相同的输出配对差异很小(<16)的字符,就像一对差异很大的字符。

假设您只使用“类似字节”的字符串(所有输入的序数值都小于256),则需要将十六进制输出填充到固定的宽度2,并更改填充零:

xored.append(hex(xored_value)[2:])

收件人:

xored.append('{:02x}'.format(xored_value))

保存一个临时字符串(hex + slice使更长的字符串然后切掉前缀,当格式字符串可以直接产生不带前缀的结果时)和零填充,宽度为2。

>

对于更多的Python代码/性能代码,还有其他可能的改进,但这足以使您的代码产生可用的结果。

旁注:运行原始代码时,xor_two_str('abc', 'ab')xor_two_str('ab', 'abc')都产生相同的输出002Try it online!),这是您所期望的(由于异或运算是可交换的,并且您循环较短的输入,将参数反转为任何调用都应产生相同的结果)。不知道为什么您认为它会产生5a。我的固定代码(Try it online!)仅产生输出00000000000200000200;正确填充,但结果不变。

相对于实际执行的工作,要进行的其他改进,手动逐字符转换以及通过余数和索引手动循环较短的输入是此代码中令人惊讶的昂贵部分。您可以做一些减少这种开销的事情,包括:

  1. Convert from str to bytes once, up-front, in bulk(通过字符转换,最快字符的运行时间大约是七分之一)
  2. 预先确定哪个字符串最短,然后use itertools.cycle根据需要对其进行扩展,然后zip直接遍历配对的字节值而不是完全索引

这可以让您:

from itertools import cycle

def xor_two_str(a,b):
    # Convert to bytes so we iterate by ordinal, determine which is longer
    short, long = sorted((a.encode('latin-1'), b.encode('latin-1')), key=len)
    xored = []
    for x, y in zip(long, cycle(short)):
        xored_value = x ^ y
        xored.append('{:02x}'.format(xored_value))
    return ''.join(xored)

或者为了使其更加简洁/快速,我们只制作了bytes对象而不转换为十六进制(并且只是为了好玩,请使用map + operator.xor来避免Python级别完全循环,将所有工作推送到CPython参考解释器中的C层,然后使用the (new in 3.5) bytes.hex method批量转换为十六进制str

from itertools import cycle
from operator import xor

def xor_two_str(a,b):
    short, long = sorted((a.encode('latin-1'), b.encode('latin-1')), key=len)
    xored = bytes(map(xor, long, cycle(short)))
    return xored.hex()