Python 2 和 3 中的分块字节(不是字符串)
Chunking bytes (not strings) in Python 2 and 3
事实证明这比我预期的要棘手。我有一个字节串:
data = b'abcdefghijklmnopqrstuvwxyz'
我想以 n 字节的块形式读取此数据。在 Python 2 下,通过对 itertools
文档中的 grouper
配方进行微小修改,这是微不足道的:
def grouper(iterable, n, fillvalue=None):
"Collect data into fixed-length chunks or blocks"
# grouper('ABCDEFG', 3, 'x') --> ABC DEF Gxx
args = [iter(iterable)] * n
return (''.join(x) for x in izip_longest(fillvalue=fillvalue, *args))
有了这个,我可以调用:
>>> list(grouper(data, 2))
并得到:
['ab', 'cd', 'ef', 'gh', 'ij', 'kl', 'mn', 'op', 'qr', 'st', 'uv', 'wx', 'yz']
在 Python 3 下,这变得更加棘手。 grouper
函数的写法
简单地摔倒了:
>>> list(grouper(data, 2))
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 5, in <genexpr>
TypeError: sequence item 0: expected str instance, int found
这是因为在 Python 3 中,当你迭代一个字节串时(比如 b'foo'
),你会得到一个整数列表,而不是字节列表:
>>> list(b'foo')
[102, 111, 111]
python 3 bytes
函数将在此处提供帮助:
def grouper(iterable, n, fillvalue=None):
"Collect data into fixed-length chunks or blocks"
# grouper('ABCDEFG', 3, 'x') --> ABC DEF Gxx
args = [iter(iterable)] * n
return (bytes(x) for x in izip_longest(fillvalue=fillvalue, *args))
使用它,我得到了我想要的:
>>> list(grouper(data, 2))
[b'ab', b'cd', b'ef', b'gh', b'ij', b'kl', b'mn', b'op', b'qr', b'st', b'uv', b'wx', b'yz']
但是(当然!)Python 2 下的 bytes
函数不运行
一样的方法。它只是 str
的别名,因此结果为:
>>> list(grouper(data, 2))
["('a', 'b')", "('c', 'd')", "('e', 'f')", "('g', 'h')", "('i', 'j')", "('k', 'l')", "('m', 'n')", "('o', 'p')", "('q', 'r')", "('s', 't')", "('u', 'v')", "('w', 'x')", "('y', 'z')"]
...这一点用处也没有。我最后写了以下内容:
def to_bytes(s):
if six.PY3:
return bytes(s)
else:
return ''.encode('utf-8').join(list(s))
def grouper(iterable, n, fillvalue=None):
"Collect data into fixed-length chunks or blocks"
# grouper('ABCDEFG', 3, 'x') --> ABC DEF Gxx
args = [iter(iterable)] * n
return (to_bytes(x) for x in izip_longest(fillvalue=fillvalue, *args))
这似乎可行,但真的是这样吗?
Funcy (a library offering various useful utilities, supporting both Python 2 and 3) offers a chunks
function 正是这样做的:
>>> import funcy
>>> data = b'abcdefghijklmnopqrstuvwxyz'
>>> list(funcy.chunks(6, data))
[b'abcdef', b'ghijkl', b'mnopqr', b'stuvwx', b'yz'] # Python 3
['abcdef', 'ghijkl', 'mnopqr', 'stuvwx', 'yz'] # Python 2.7
或者,您可以在程序中包含一个简单的实现(与 Python 2.7 和 3 兼容):
def chunked(size, source):
for i in range(0, len(source), size):
yield source[i:i+size]
它的行为相同(至少对于您的数据而言;Funcy 的 chunks
也适用于迭代器,但这个不行):
>>> list(chunked(6, data))
[b'abcdef', b'ghijkl', b'mnopqr', b'stuvwx', b'yz'] # Python 3
['abcdef', 'ghijkl', 'mnopqr', 'stuvwx', 'yz'] # Python 2.7
如果您的字符串长度可以被 n 整除或者您将非空字符串作为填充值传递,则将 bytes
与 bytearray
一起使用对两者都有效:
def grouper(iterable, n, fillvalue=None):
"Collect data into fixed-length chunks or blocks"
# grouper('ABCDEFG', 3, 'x') --> ABC DEF Gxx
args = [iter(iterable)] * n
return ((bytes(bytearray(x))) for x in zip_longest(fillvalue=fillvalue, *args))
py3:
In [20]: import sys
In [21]: sys.version
Out[21]: '3.4.3 (default, Oct 14 2015, 20:28:29) \n[GCC 4.8.4]'
In [22]: print(list(grouper(data,2)))
[b'ab', b'cd', b'ef', b'gh', b'ij', b'kl', b'mn', b'op', b'qr', b'st', b'uv', b'wx', b'yz']
Py2:
In [6]: import sys
In [7]: sys.version
Out[7]: '2.7.6 (default, Jun 22 2015, 17:58:13) \n[GCC 4.8.2]'
In [8]: print(list(grouper(data,2)))
['ab', 'cd', 'ef', 'gh', 'ij', 'kl', 'mn', 'op', 'qr', 'st', 'uv', 'wx', 'yz']
如果您传递了一个空字符串,您可以过滤掉它们:
def grouper(iterable, n, fillvalue=None):
"Collect data into fixed-length chunks or blocks"
# grouper('ABCDEFG', 3, 'x') --> ABC DEF Gxx
args = [iter(iterable)] * n
return ((bytes(bytearray(filter(None, x)))) for x in zip_longest(fillvalue=fillvalue, *args))
这适用于任何长度的字符串。
In [29]: print(list(grouper(data,4)))
[b'abcd', b'efgh', b'ijkl', b'mnop', b'qrst', b'uvwx', b'yz']
In [30]: print(list(grouper(data,3)))
[b'abc', b'def', b'ghi', b'jkl', b'mno', b'pqr', b'stu', b'vwx', b'yz']
事实证明这比我预期的要棘手。我有一个字节串:
data = b'abcdefghijklmnopqrstuvwxyz'
我想以 n 字节的块形式读取此数据。在 Python 2 下,通过对 itertools
文档中的 grouper
配方进行微小修改,这是微不足道的:
def grouper(iterable, n, fillvalue=None):
"Collect data into fixed-length chunks or blocks"
# grouper('ABCDEFG', 3, 'x') --> ABC DEF Gxx
args = [iter(iterable)] * n
return (''.join(x) for x in izip_longest(fillvalue=fillvalue, *args))
有了这个,我可以调用:
>>> list(grouper(data, 2))
并得到:
['ab', 'cd', 'ef', 'gh', 'ij', 'kl', 'mn', 'op', 'qr', 'st', 'uv', 'wx', 'yz']
在 Python 3 下,这变得更加棘手。 grouper
函数的写法
简单地摔倒了:
>>> list(grouper(data, 2))
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 5, in <genexpr>
TypeError: sequence item 0: expected str instance, int found
这是因为在 Python 3 中,当你迭代一个字节串时(比如 b'foo'
),你会得到一个整数列表,而不是字节列表:
>>> list(b'foo')
[102, 111, 111]
python 3 bytes
函数将在此处提供帮助:
def grouper(iterable, n, fillvalue=None):
"Collect data into fixed-length chunks or blocks"
# grouper('ABCDEFG', 3, 'x') --> ABC DEF Gxx
args = [iter(iterable)] * n
return (bytes(x) for x in izip_longest(fillvalue=fillvalue, *args))
使用它,我得到了我想要的:
>>> list(grouper(data, 2))
[b'ab', b'cd', b'ef', b'gh', b'ij', b'kl', b'mn', b'op', b'qr', b'st', b'uv', b'wx', b'yz']
但是(当然!)Python 2 下的 bytes
函数不运行
一样的方法。它只是 str
的别名,因此结果为:
>>> list(grouper(data, 2))
["('a', 'b')", "('c', 'd')", "('e', 'f')", "('g', 'h')", "('i', 'j')", "('k', 'l')", "('m', 'n')", "('o', 'p')", "('q', 'r')", "('s', 't')", "('u', 'v')", "('w', 'x')", "('y', 'z')"]
...这一点用处也没有。我最后写了以下内容:
def to_bytes(s):
if six.PY3:
return bytes(s)
else:
return ''.encode('utf-8').join(list(s))
def grouper(iterable, n, fillvalue=None):
"Collect data into fixed-length chunks or blocks"
# grouper('ABCDEFG', 3, 'x') --> ABC DEF Gxx
args = [iter(iterable)] * n
return (to_bytes(x) for x in izip_longest(fillvalue=fillvalue, *args))
这似乎可行,但真的是这样吗?
Funcy (a library offering various useful utilities, supporting both Python 2 and 3) offers a chunks
function 正是这样做的:
>>> import funcy
>>> data = b'abcdefghijklmnopqrstuvwxyz'
>>> list(funcy.chunks(6, data))
[b'abcdef', b'ghijkl', b'mnopqr', b'stuvwx', b'yz'] # Python 3
['abcdef', 'ghijkl', 'mnopqr', 'stuvwx', 'yz'] # Python 2.7
或者,您可以在程序中包含一个简单的实现(与 Python 2.7 和 3 兼容):
def chunked(size, source):
for i in range(0, len(source), size):
yield source[i:i+size]
它的行为相同(至少对于您的数据而言;Funcy 的 chunks
也适用于迭代器,但这个不行):
>>> list(chunked(6, data))
[b'abcdef', b'ghijkl', b'mnopqr', b'stuvwx', b'yz'] # Python 3
['abcdef', 'ghijkl', 'mnopqr', 'stuvwx', 'yz'] # Python 2.7
如果您的字符串长度可以被 n 整除或者您将非空字符串作为填充值传递,则将 bytes
与 bytearray
一起使用对两者都有效:
def grouper(iterable, n, fillvalue=None):
"Collect data into fixed-length chunks or blocks"
# grouper('ABCDEFG', 3, 'x') --> ABC DEF Gxx
args = [iter(iterable)] * n
return ((bytes(bytearray(x))) for x in zip_longest(fillvalue=fillvalue, *args))
py3:
In [20]: import sys
In [21]: sys.version
Out[21]: '3.4.3 (default, Oct 14 2015, 20:28:29) \n[GCC 4.8.4]'
In [22]: print(list(grouper(data,2)))
[b'ab', b'cd', b'ef', b'gh', b'ij', b'kl', b'mn', b'op', b'qr', b'st', b'uv', b'wx', b'yz']
Py2:
In [6]: import sys
In [7]: sys.version
Out[7]: '2.7.6 (default, Jun 22 2015, 17:58:13) \n[GCC 4.8.2]'
In [8]: print(list(grouper(data,2)))
['ab', 'cd', 'ef', 'gh', 'ij', 'kl', 'mn', 'op', 'qr', 'st', 'uv', 'wx', 'yz']
如果您传递了一个空字符串,您可以过滤掉它们:
def grouper(iterable, n, fillvalue=None):
"Collect data into fixed-length chunks or blocks"
# grouper('ABCDEFG', 3, 'x') --> ABC DEF Gxx
args = [iter(iterable)] * n
return ((bytes(bytearray(filter(None, x)))) for x in zip_longest(fillvalue=fillvalue, *args))
这适用于任何长度的字符串。
In [29]: print(list(grouper(data,4)))
[b'abcd', b'efgh', b'ijkl', b'mnop', b'qrst', b'uvwx', b'yz']
In [30]: print(list(grouper(data,3)))
[b'abc', b'def', b'ghi', b'jkl', b'mno', b'pqr', b'stu', b'vwx', b'yz']