问题
I need to encrypt / decrypt a file using xor with Python 3, I have a code that works fine in Python 2, but when trying to adapt it to Python 3, gives me some errors that I can't solve.
This code works fine in Python 2.7:
from itertools import cycle
def xore(data, key):
return ''.join(chr(ord(a) ^ ord(b)) for (a, b) in zip(data, cycle(key)))
with open('inputfile.jpg', 'rb') as encry, open('outputfile.jpg', 'wb') as decry:
decry.write(xore(encry.read(), 'anykey'))
The error when trying to run unchanged in python 3:
Traceback (most recent call last):
File "ask.py", line 8, in <module>
decry.write(xore(encry.read(), 'anykey'))
File "ask.py", line 5, in xore
return ''.join(chr(ord(a) ^ ord(b)) for (a, b) in zip(data, cycle(key)))
File "ask.py", line 5, in <genexpr>
return ''.join(chr(ord(a) ^ ord(b)) for (a, b) in zip(data, cycle(key)))
TypeError: ord() expected string of length 1, but int found
Please if someone could explain and help me adapt this code to Python 3.
回答1:
a
is already an int
so you would need to remove the call to ord(a)
:
def xore(data, key):
return ''.join(chr(a ^ ord(b)) for (a, b) in zip(data, cycle(key)))
You will not be able to write the joined string to your outfile without a .encode("utf-8")
which will not give you any usable output so not sure what you are actually trying to achieve.
You can see when you index a byte string you get an int in python3:
n [1]: s = b"foo"
In [2]: s[0]
Out[2]: 102 # f
Where in python2 you get the str/char:
In [1]: s = b"foo"
In [2]: s[0]
Out[2]: 'f'
iterating or calling next also gives you the integer value:
In [12]: it = iter(s)
In [13]: next(it)
Out[13]: 102
In [14]: for ele in s:
print(ele)
....:
102
111
111
In python2 you would just get each character. So in your code as you iterate over the bytes objects returned from encry.read()
you are getting the integer values so ord(some_int)
obviously fails.
There is a thorough explanation text-versus-binary-data which explains the differences between python2 and python3 a snippet of which is:
As part of this dichotomy you also need to be careful about opening files. Unless you have been working on Windows, there is a chance you have not always bothered to add the b mode when opening a binary file (e.g., rb for binary reading). Under Python 3, binary files and text files are clearly distinct and mutually incompatible; see the io module for details. Therefore, you must make a decision of whether a file will be used for binary access (allowing to read and/or write binary data) or text access (allowing to read and/or write text data). You should also use io.open() for opening files instead of the built-in open() function as the io module is consistent from Python 2 to 3 while the built-in open() function is not (in Python 3 it’s actually io.open()).
The constructors of both str and bytes have different semantics for the same arguments between Python 2 & 3. Passing an integer to bytes in Python 2 will give you the string representation of the integer:
bytes(3) == '3'
. But in Python 3, an integer argument to bytes will give you a bytes object as long as the integer specified, filled with null bytes:bytes(3) == b'\x00\x00\x00'
. A similar worry is necessary when passing a bytes object to str. In Python 2 you just get the bytes object back:str(b'3') == b'3'
. But in Python 3 you get the string representation of the bytes object:str(b'3') == "b'3'"
.Finally, the indexing of binary data requires careful handling (slicing does not require any special handling). In Python 2,
b'123'[1] == b'2'
while in Python 3b'123'[1] == 50
. Because binary data is simply a collection of binary numbers, Python 3 returns the integer value for the byte you index on. But in Python 2 because bytes == str, indexing returns a one-item slice of bytes. The six project has a function named six.indexbytes() which will return an integer like in Python 3: six.indexbytes(b'123', 1).
来源:https://stackoverflow.com/questions/32672181/xor-encryption-decryption-of-a-file-with-python-3