在Python 3中将字符串转换为字节的最佳方法?

Mar*_*som 734 python string character-encoding python-3.x

似乎有两种不同的方法将字符串转换为字节,如TypeError的答案所示:'str'不支持缓冲区接口

哪种方法更好或更好Pythonic?或者只是个人喜好?

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

b = mystring.encode('utf-8')
Run Code Online (Sandbox Code Playgroud)

agf*_*agf 517

如果您查看文档bytes,它会指出bytearray:

bytearray([source [,encoding [,errors]]])

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

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

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

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

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

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

如果没有参数,则会创建一个大小为0的数组.

所以bytes可以做的不仅仅是编码一个字符串.它是Pythonic,它允许你用任何类型的有意义的源参数调用构造函数.

对于编码字符串,我认为这some_string.encode(encoding)比使用构造函数更Pythonic,因为它是最自我的文档 - "使用此字符串并使用此编码对其进行编码"比bytes(some_string, encoding)使用更清晰- 当您使用时没有明确的动词构造函数.

编辑:我检查了Python源代码.如果你将unicode字符串传递给bytes使用CPython,它会调用PyUnicode_AsEncodedString,这是执行encode; 所以如果你打电话给encode自己,你只是跳过一个间接的水平.

另外,请参阅Serdalis的评论 - unicode_string.encode(encoding)也更像Pythonic,因为它的反面byte_string.decode(encoding)和对称性很好.

  • +1有一个好的参数和来自python文档的引用.当你想要你的字符串回来时,`unicode_string.encode(encoding)`与`bytearray.decode(encoding)`很好地匹配. (66认同)
  • @EugeneHomyakov这与`bytearray`无关,除了`bytes`的文档没有提供细节,他们只是说"这是`bytearray`的不可变版本"所以我必须引用它. (7认同)
  • `tl;dr` 会有帮助的 (5认同)
  • 当需要一个可变对象时,使用`bytearray`。简单的`str`↔`bytes`转换不需要它。 (4认同)
  • 请注意,如果您尝试将二进制数据转换为字符串,您很可能需要使用类似 `byte_string.decode('latin-1')` 的内容,因为 `utf-8` 不包括整个范围 0x00 到 0xFF (0-255),请查看 python [文档](https://docs.python.org/3/library/codecs.html#standard-encodings) 了解更多信息。 (3认同)
  • 一些带有输出的示例会很有帮助。 (2认同)

has*_*zmi 279

它比想象的容易:

my_str = "hello world"
my_str_as_bytes = str.encode(my_str)
type(my_str_as_bytes) # ensure it is byte representation
my_decoded_str = my_str_as_bytes.decode()
type(my_decoded_str) # ensure it is string representation
Run Code Online (Sandbox Code Playgroud)

  • 他知道怎么做,他只是问哪条路更好.请重新阅读这个问题. (27认同)
  • 仅供参考:str.decode(bytes)对我不起作用(Python 3.3.3说"类型对象'str'没有属性'decode'")我使用了bytes.decode()代替 (24认同)
  • @Mike:使用`obj.method()`语法而不是`cls.method(obj)`语法,即使用`bytestring = unicode_text.encode(encoding)`和`unicode_text = bytestring.decode(encoding)`. (6认同)
  • ... 即你不必要地创建一个未绑定的方法,然后调用它传递 `self` 作为第一个参数 (3认同)
  • @KolobCanyon 这个问题已经展示了正确的方法——调用 `encode` 作为字符串上的绑定方法。此答案建议您改为调用未绑定方法并将字符串传递给它。这是答案中唯一的新信息,这是错误的。 (2认同)

Ant*_*ala 107

绝对最好的办法既不是2,但第3位.自Python 3.0以来默认的第一个参数.因此最好的方法是encode 'utf-8'

b = mystring.encode()
Run Code Online (Sandbox Code Playgroud)

这也会更快,因为默认参数不会"utf-8"导致C代码中的字符串,但是NULL,检查速度快得多!

这里有一些时间:

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
Run Code Online (Sandbox Code Playgroud)

尽管有警告,但在重复运行后时间非常稳定 - 偏差仅为~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)
Run Code Online (Sandbox Code Playgroud)

  • @MarkRansom 那么你实际使用了多少次 `int(s, 10)` ;-) (5认同)
  • Python 的 Zen 声明显式优于隐式,这意味着显式的“utf-8”参数是首选。但你已经明确表明,省略参数会更快。这使得这是一个很好的答案,即使它不是最好的答案。 (4认同)
  • 这里只有相当大的区别,因为 (a) 字符串是纯 ASCII,这意味着内部存储已经是 UTF-8 版本,因此查找编解码器几乎是唯一涉及的成本,并且 (b) 字符串很小,因此即使您确实必须进行编码,也不会有太大区别。试试看,比如说,`'\u00012345'*10000`。在我的笔记本电脑上,两者都需要 28.8us;额外的 50ns 可能会在舍入误差中丢失。当然,这是一个非常极端的例子——但 `'abc'` 在相反的方向上也同样极端。 (2认同)

Bre*_*ent 26

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

您有一个保存在 str 变量中的原始 unicode 序列:

s_str: str = "\x00\x01\x00\xc0\x01\x00\x00\x00\x04"
Run Code Online (Sandbox Code Playgroud)

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

s_bytes: bytes = b'\x00\x01\x00\xc0\x01\x00\x00\x00\x04'
Run Code Online (Sandbox Code Playgroud)

解决方案:

s_new: bytes = bytes(s, encoding="raw_unicode_escape")
Run Code Online (Sandbox Code Playgroud)

参考(向上滚动查看标准编码):

Python 特定编码

  • 这实际上正是我正在寻找的。我不知道如何更好地表达我的问题。:)谢谢@布伦特! (7认同)
  • 这是我需要的答案,来自谷歌搜索“python 3 将 str 转换为字节二进制”,这是最重要的结果,看起来很有希望。还有更有趣的问题——比如如何将 unicode 字符串转换为常规字符串 (python 2.7) :p (4认同)

Jas*_*gan 5

Python 3“内存视图方式怎么样?

Memoryview 是 byte/bytearray 和 struct 模块的一种混搭,有几个好处。

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

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

memoryview(b"some bytes").tolist()

[115, 111, 109, 101, 32, 98, 121, 116, 101, 115]
Run Code Online (Sandbox Code Playgroud)

或者对于 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 same
memoryview("\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]
Run Code Online (Sandbox Code Playgroud)

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

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]

Run Code Online (Sandbox Code Playgroud)

小心的话。请注意对多于一个字节的数据的字节顺序的多种解释:

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]

Run Code Online (Sandbox Code Playgroud)

不确定这是故意的还是错误,但它让我发现了!!

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

  • 您所做的只是在问题中建议的内容之上添加另一层。我完全不明白这有什么用处。 (3认同)