I would like to perform a bitwise exclusive or of two strings in python, but xor of strings are not allowed in python. How can I do it ?
-
4What do you mean by "bitwise exclusive or of words"? Doesn't make sense to me... – Olivier Verdier Apr 10 '10 at 09:44
-
Related: [Simple Python Challenge: Fastest Bitwise XOR on Data Buffers](http://stackoverflow.com/q/2119761/4279) – jfs Sep 19 '16 at 10:08
-
3if this question was asked in 2018, it would never be given the chance to make it this far. – DeerSpotter Nov 27 '18 at 13:36
12 Answers
You can convert the characters to integers and xor those instead:
l = [ord(a) ^ ord(b) for a,b in zip(s1,s2)]
Here's an updated function in case you need a string as a result of the XOR:
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))
See it working online: ideone

- 811,555
- 193
- 1,581
- 1,452
-
21I disagree. If doing cryptographic or other similar data manipulation operations in Python you want to be able to do this on strings of bytes. In my opinion Python3 should support this operation on byte strings. – Omnifarious Jun 27 '12 at 02:36
-
8or something like `bytes(x ^ y for x, y in zip(s1, s2))` in python3 as you may see below :) http://stackoverflow.com/questions/2612720/how-to-do-bitwise-exclusive-or-of-two-strings-in-python/28481974#28481974 – yota Feb 12 '15 at 15:59
If you want to operate on bytes or words then you'll be better to use Python's array type instead of a string. If you are working with fixed length blocks then you may be able to use H or L format to operate on words rather than bytes, but I just used 'B' for this example:
>>> 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'

- 92,073
- 11
- 122
- 156
-
6I believe this is the answer that most probably corresponds to what the OP wanted to ask. – tzot Apr 10 '10 at 12:53
For bytearrays you can directly use 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')

- 459
- 3
- 4
Here is your string XOR'er, presumably for some mild form of encryption:
>>> 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!
Note that this is an extremely weak form of encryption. Watch what happens when given a blank string to encode:
>>> codebreak = xorWord(" ", code)
>>> print codebreak
SECRET

- 62,419
- 16
- 94
- 130
-
6XOR encryption is unbreakable if key is larger than message. http://en.wikipedia.org/wiki/One_time_pad – Michał Zieliński Jul 13 '13 at 07:47
-
8
-
-
-
when i try to do i fail myserably, what am i doing wrong?: decrypt = xorWord(src[3:].decode("hex"),code.decode("hex")) – DeerSpotter Nov 21 '18 at 01:56
-
2@DeerSpotter There are several problems here, more than can be addressed in this discussion thread. Open a new question, and include the input src and the output error message. "I fail miserably" is not enough for anyone to help you with. Also, be sure you are using Python3. – PaulMcG Nov 21 '18 at 05:12
-
@PaulMcG i am currently banned from asking questions. This is my frustration with stack overflow. I have legit questions and there are so many grammer peace keepers here it blows my mind that someone can ask a good question. – DeerSpotter Nov 27 '18 at 13:33
the one liner for python3 is :
def bytes_xor(a, b) :
return bytes(x ^ y for x, y in zip(a, b))
where a
, b
and the returned value are bytes()
instead of str()
of course
can't be easier, I love python3 :)

- 2,020
- 22
- 37
If the strings are not even of equal length, you can use this
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)])])

- 171
- 1
- 1
- 8
def strxor (s0, s1):
l = [ chr ( ord (a) ^ ord (b) ) for a,b in zip (s0, s1) ]
return ''.join (l)
(Based on Mark Byers answer.)

- 51
- 1
-
1when s0 and s1 are not the same size, we either use izip_longest or itertools.islice over itertools.cycle of the two strings – Muayyad Alsadi Dec 30 '12 at 10:30
Do you mean something like this:
s1 = '00000001'
s2 = '11111110'
int(s1,2) ^ int(s2,2)

- 31,822
- 31
- 91
- 141
-
output of this is long: >>> type(int(bin_c,2) ^ int(bin_m, 2))
– Dmitry Zagorulkin Jan 24 '13 at 09:43
I've found that the ''.join(chr(ord(a)^ord(b)) for a,b in zip(s,m)) method is pretty slow. Instead, I've been doing this:
fmt = '%dB' % len(source)
s = struct.unpack(fmt, source)
m = struct.unpack(fmt, xor_data)
final = struct.pack(fmt, *(a ^ b for a, b in izip(s, m)))

- 2,166
- 11
- 7
-
you could [use `bytearray()` instead of pack/unpack](http://stackoverflow.com/a/20570990/4279). – jfs Nov 07 '16 at 12:40
-
Indeed, this is about 26% faster than *struct.pack*: `def strxor(a, b, izip=itertools.izip, ba=bytearray): return str(ba((a ^ b for a, b in izip(ba(a), ba(b)))))` . Please note that *bytearray* was introduced in Python 2.6, and *struct.pack* works in earlier versions of Python (including 2.4). – pts Dec 25 '18 at 15:38
Below illustrates XORing string s with m, and then again to reverse the process:
>>> 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'
>>>

- 166,664
- 26
- 169
- 251
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)])

- 6,764
- 6
- 33
- 31
Based on William McBrine's answer, here is a solution for fixed-length strings which is 9% faster for my use case:
import itertools
import struct
def make_strxor(size):
def strxor(a, b, izip=itertools.izip, pack=struct.pack, unpack=struct.unpack, fmt='%dB' % size):
return pack(fmt, *(a ^ b for a, b in izip(unpack(fmt, a), unpack(fmt, b))))
return strxor
strxor_3 = make_strxor(3)
print repr(strxor_3('foo', 'bar'))

- 80,836
- 20
- 110
- 183