在 Python3中将字符串转换为字节的最佳方法是什么?

TypeError: str & # 39; 不支持缓冲区接口 建议了两种将字符串转换为字节的可能方法:

b = bytes(mystring, 'utf-8')


b = mystring.encode('utf-8')

哪种方法比较 Python 化?

2380652 次浏览

如果你查看bytes的文档,它会指向#1

字节数组([源[,编码[,错误]]])

返回一个新的字节数组。字节数组类型是范围为0<=x<256的可变整数序列。它具有可变序列的大多数常用方法,在可变序列类型中进行了描述,以及字节类型具有的大多数方法,请参阅字节和字节数组方法。

可选的source参数可用于以几种不同的方式初始化数组:

如果它是一个字符串,您还必须给出编码(以及可选的错误)参数;然后使用str.encode()将字符串转换为字节。

如果它是一个整数,数组将具有该大小并将使用空字节初始化。

如果它是符合缓冲区接口的对象,则将使用该对象的只读缓冲区来初始化字节数组。

如果它是可迭代的,它必须是0<=x<256范围内的整数的可迭代,这些整数用作数组的初始内容。

如果没有参数,则创建大小为0的数组。

所以bytes可以做的不仅仅是编码字符串。它是Pythonic,它允许您使用任何类型的源参数调用构造函数。

对于编码字符串,我认为some_string.encode(encoding)比使用构造函数更Pythonic,因为它是最自记录的——“获取此字符串并使用此编码对其进行编码”比bytes(some_string, encoding)更清晰——使用构造函数时没有明确的动词。

我检查了Python源代码。如果您使用CPython将Unicode字符串传递给bytes,它会调用PyUnicode_AsEncodedString,这是encode的实现;所以如果您自己调用encode,您只是跳过了一个间接级别。

此外,请参阅Serdalis的评论-unicode_string.encode(encoding)也更Pythonic,因为它的逆是byte_string.decode(encoding)并且对称性很好。

这比想象的要容易:

my_str = "hello world"my_str_as_bytes = str.encode(my_str)print(type(my_str_as_bytes)) # ensure it is byte representationmy_decoded_str = my_str_as_bytes.decode()print(type(my_decoded_str)) # ensure it is string representation

您可以通过打印类型来验证。请参阅下面的输出。

<class 'bytes'><class 'str'>

绝对最好的方法不是2,而是3。自Python 3.0以来#0默认为'utf-8'的第一个参数。因此最好的方法是

b = mystring.encode()

这也会更快,因为默认参数的结果不是C代码中的字符串"utf-8",而是#1,检查速度快

以下是一些时间:

In [1]: %timeit -r 10 'abc'.encode('utf-8')The slowest run took 38.07 times longer than the fastest.This could mean that an intermediate result is being cached.10000000 loops, best of 10: 183 ns per loop
In [2]: %timeit -r 10 'abc'.encode()The slowest run took 27.34 times longer than the fastest.This could mean that an intermediate result is being cached.10000000 loops, best of 10: 137 ns per loop

尽管有警告,但在反复运行后,时间非常稳定-偏差仅为约2%。


使用不带参数的encode()与Python 2不兼容,因为在Python 2中,默认字符编码为ASCII码

>>> 'äöä'.encode()Traceback (most recent call last):File "<stdin>", line 1, in <module>UnicodeDecodeError: 'ascii' codec can't decode byte 0xc3 in position 0: ordinal not in range(128)

回答一个稍微不同的问题:

你有一个原始Unicode序列,它被保存到一个str变量中:

s_str: str = "\x00\x01\x00\xc0\x01\x00\x00\x00\x04"

您需要能够获取该Unicode的字节文字(对于struct.unpack()等)

s_bytes: bytes = b'\x00\x01\x00\xc0\x01\x00\x00\x00\x04'

解决方案:

s_new: bytes = bytes(s, encoding="raw_unicode_escape")

参考(向上滚动以获取标准编码):

Python特定编码

Python 3'内存视图'怎么样?

Memoryview是字节/字节数组和struct模块的一种混杂,有几个好处。

  • 不仅限于文本和字节,还可以处理16位和32位单词
  • 与endianness合作
  • 为链接的C/C++函数和数据提供非常低的开销接口

最简单的例子,对于字节数组:

memoryview(b"some bytes").tolist()
[115, 111, 109, 101, 32, 98, 121, 116, 101, 115]

或者对于Unicode字符串(转换为字节数组)

memoryview(bytes("\u0075\u006e\u0069\u0063\u006f\u0064\u0065\u0020", "UTF-16")).tolist()
[255, 254, 117, 0, 110, 0, 105, 0, 99, 0, 111, 0, 100, 0, 101, 0, 32, 0]
#Another way to do the samememoryview("\u0075\u006e\u0069\u0063\u006f\u0064\u0065\u0020".encode("UTF-16")).tolist()
[255, 254, 117, 0, 110, 0, 105, 0, 99, 0, 111, 0, 100, 0, 101, 0, 32, 0]

也许你需要单词而不是字节?

memoryview(bytes("\u0075\u006e\u0069\u0063\u006f\u0064\u0065\u0020", "UTF-16")).cast("H").tolist()
[65279, 117, 110, 105, 99, 111, 100, 101, 32]
memoryview(b"some  more  data").cast("L").tolist()
[1701670771, 1869422624, 538994034, 1635017060]

谨慎之言。小心使用多个字节的数据对字节顺序进行多种解释:

txt = "\u0075\u006e\u0069\u0063\u006f\u0064\u0065\u0020"for order in ("", "BE", "LE"):mv = memoryview(bytes(txt, f"UTF-16{order}"))print(mv.cast("H").tolist())
[65279, 117, 110, 105, 99, 111, 100, 101, 32][29952, 28160, 26880, 25344, 28416, 25600, 25856, 8192][117, 110, 105, 99, 111, 100, 101, 32]

不确定这是故意的还是bug但它抓住了我!

该示例使用UTF-16,有关编解码器的完整列表,请参阅Python 3.10中的编解码器注册表