在Python中如何对两个字符串进行按位异或运算?

55

我想在Python中对两个字符串执行位异或操作,但是Python不允许对字符串进行异或运算。我该怎么做?


4
“按位异或词”的意思是什么?我不太理解... - Olivier Verdier
3
如果这个问题是在2018年提出的话,它永远不可能有机会走到现在这一步。 - DeerSpotter
12个回答

70

您可以将字符转换为整数,然后将它们异或:

l = [ord(a) ^ ord(b) for a,b in zip(s1,s2)]

如果你需要一个字符串作为异或运算的结果,这里是一个更新的函数:

def sxor(s1,s2):    
    # convert strings to a list of character pair tuples
    # go through each tuple, converting them to ASCII code (ord)
    # perform exclusive or on the ASCII code
    # then convert the result back to ASCII (chr)
    # merge the resulting array of characters as a string
    return ''.join(chr(ord(a) ^ ord(b)) for a,b in zip(s1,s2))

在线查看演示: ideone


21
我不同意。如果在Python中进行加密或其他类似的数据操作,您需要能够对字节串执行此操作。我认为Python3应该支持在字节串上进行此操作。 - Omnifarious
8
在Python3中可以使用类似于bytes(x ^ y for x, y in zip(s1, s2))的代码进行两个字符串的按位异或操作。你可以在下面的链接中查看相关信息:https://dev59.com/Z3E85IYBdhLWcg3wyGp_#28481974 - yota

28

如果你想操作字节或单词,那么最好使用Python的数组类型而不是字符串。如果你正在处理固定长度的块,则可以使用H或L格式来操作单词而不是字节,但我在这个示例中只使用了'B':

>>> import array
>>> a1 = array.array('B', 'Hello, World!')
>>> a1
array('B', [72, 101, 108, 108, 111, 44, 32, 87, 111, 114, 108, 100, 33])
>>> a2 = array.array('B', ('secret'*3))
>>> for i in range(len(a1)):
    a1[i] ^= a2[i]


>>> a1.tostring()
';\x00\x0f\x1e\nXS2\x0c\x00\t\x10R'

6
我相信这很可能是与提问者想要询问的问题最相符的答案。 - tzot

17
对于字节数组,您可以直接使用XOR:
>>> b1 = bytearray("test123")
>>> b2 = bytearray("321test")
>>> b = bytearray(len(b1))
>>> for i in range(len(b1)):
...   b[i] = b1[i] ^ b2[i]

>>> b
bytearray(b'GWB\x00TAG')

类型错误:没有编码的字符串参数 - Meiogordo

15

Python3的一行代码是:

def bytes_xor(a, b) :
    return bytes(x ^ y for x, y in zip(a, b))

当然,a, b和返回的值都应该是bytes()而不是str()

再简单不过了,我喜欢Python3 :)


15

这是一个字符串异或器,可能用于某种轻度的加密:

>>> src = "Hello, World!"
>>> code = "secret"
>>> xorWord = lambda ss,cc: ''.join(chr(ord(s)^ord(c)) for s,c in zip(ss,cc*100))
>>> encrypt = xorWord(src, code)
>>> encrypt
';\x00\x0f\x1e\nXS2\x0c\x00\t\x10R'
>>> decrypt = xorWord(encrypt,code)
>>> print decrypt
Hello, World!

请注意,这是一种非常弱的加密方式。当给定一个空字符串进行编码时,会发生什么,请看下面的例子:
>>> codebreak = xorWord("      ", code)
>>> print codebreak
SECRET

6
如果密钥长度大于消息长度,XOR加密就无法被破解。参考链接:http://en.wikipedia.org/wiki/One_time_pad - Michał Zieliński
8
只有在你只使用钥匙一次的情况下才是正确的。 - Bruce Barnett
准确来说,只有在密钥真正是随机和不可预测的情况下才是如此! - Atomix
2
@DeerSpotter 这里有几个问题,超出了本讨论线程的范围。请开一个新问题,并包括输入源和输出错误信息。"我失败了"这样的描述是不足以帮助你的。另外,请确保你正在使用Python3。 - PaulMcG
@PaulMcG 我目前被禁止提问。这是我对Stack Overflow的沮丧。我有合理的问题,但有太多语法警察在这里,让我感到惊讶的是,有人能提出一个好问题。 - DeerSpotter
显示剩余2条评论

4
def strxor (s0, s1):
  l = [ chr ( ord (a) ^ ord (b) ) for a,b in zip (s0, s1) ]
  return ''.join (l)

(基于Mark Byers的回答。)

1
当s0和s1的大小不同时,我们可以使用izip_longest或itertools.islice覆盖两个字符串的itertools.cycle。 - Muayyad Alsadi

4
如果字符串长度不相等,您可以使用以下方法:
def strxor(a, b):     # xor two strings of different lengths
    if len(a) > len(b):
        return "".join([chr(ord(x) ^ ord(y)) for (x, y) in zip(a[:len(b)], b)])
    else:
        return "".join([chr(ord(x) ^ ord(y)) for (x, y) in zip(a, b[:len(a)])])

2
你的意思是这样吗:
s1 = '00000001'
s2 = '11111110'
int(s1,2) ^ int(s2,2)

这段代码的输出很长:>>> type(int(bin_c,2) ^ int(bin_m, 2)) <type 'long'> - Dmitry Zagorulkin

1

以下演示了用 m 对字符串 s 进行异或,然后再次执行此过程以进行反向操作:

>>> s='hello, world'
>>> m='markmarkmark'
>>> s=''.join(chr(ord(a)^ord(b)) for a,b in zip(s,m))
>>> s
'\x05\x04\x1e\x07\x02MR\x1c\x02\x13\x1e\x0f'
>>> s=''.join(chr(ord(a)^ord(b)) for a,b in zip(s,m))
>>> s
'hello, world'
>>>

1
def xor_strings(s1, s2):
    max_len = max(len(s1), len(s2))
    s1 += chr(0) * (max_len - len(s1))
    s2 += chr(0) * (max_len - len(s2))
    return ''.join([chr(ord(c1) ^ ord(c2)) for c1, c2 in zip(s1, s2)])

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接