how to do bitwise exclusive or of two strings in python?

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 ?

Answers:

Thank you for visiting the Q&A section on Magenaut. Please note that all the answers may not help you solve the issue immediately. So please treat them as advisements. If you found the post helpful (or not), leave a comment & I’ll get back to you as soon as possible.

Method 1

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

Method 2

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()
';x00x0fx1enXS2x0cx00tx10R'

Method 3

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'GWBx00TAG')

Method 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
';x00x0fx1enXS2x0cx00tx10R'
>>> 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

Method 5

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 🙂

Method 6

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.)

Method 7

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)])])

Method 8

Do you mean something like this:

s1 = '00000001'
s2 = '11111110'
int(s1,2) ^ int(s2,2)

Method 9

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
'x05x04x1ex07x02MRx1cx02x13x1ex0f'
>>> s=''.join(chr(ord(a)^ord(b)) for a,b in zip(s,m))
>>> s
'hello, world'
>>>

Method 10

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)])

Method 11

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)))

Method 12

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'))


All methods was sourced from stackoverflow.com or stackexchange.com, is licensed under cc by-sa 2.5, cc by-sa 3.0 and cc by-sa 4.0

0 0 votes
Article Rating
Subscribe
Notify of
guest

0 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
0
Would love your thoughts, please comment.x
()
x