什么是Python中的“bytestring”(`bytes`数据类型)?

vi4fp9gy  于 2023-04-04  发布在  Python
关注(0)|答案(4)|浏览(140)

Python中的“字节串”到底是什么?bytes类型是什么,它在内部是如何工作的?
我的理解是,有正常的“ASCII字符串”,它存储一系列“字符”,这些“字符”是“ASCII值”,范围从0到255,每个数字代表一个字符。同样,我理解Unicode使用8位或16位表示每个字符。
给予一个更清楚的例子:我想是的

>>> 'a'.encode()
b'a'

结果是存储一个字节的bytes
然而,我被告知bytes表示一个不可变的字节序列 *,没有特定的解释 *。那么...为什么我可以读“a”
如果我使用命令行查看字符的ASCII值:

$ printf "%d\n" "'a"
97

这是有道理的。如果我们将数字97解释为ASCII,那么我们得到的是字母a。类似地,二进制值-扩展到8位-看起来像01100001
那么,为什么'a'.encode()看起来像b'a',而不是b'97'b'01100001'(底层位模式)?为什么它看起来像ASCII一样?
因此,如果我将bytes写入以二进制模式打开的文件:

with open('testbytestring.txt', 'wb') as f:
    f.write(b'helloworld')

我仍然在文件中看到人类可读的文本helloworld!为什么会这样?

dced5bon

dced5bon1#

一个常见的误解是文本是ASCII或UTF-8或Windows-1252,因此字节是文本。
文本只是文本,就像图像只是图像一样。将文本或图像存储到磁盘的问题是将数据编码为字节序列的问题。有很多方法可以将图像编码为字节:JPEGPNGSVG,以及许多编码文本、ASCII、UTF-8或Windows-1252的方法。
一旦编码发生,字节就只是字节,不再是图像;他们忘记了他们所代表的颜色;尽管图像格式解码器可以恢复这些信息。字节同样忘记了它们曾经是的字母。事实上,字节根本不记得它们是图像还是文本。只有带外知识(文件名,媒体标题等)才能猜测这些字节的含义,甚至可能是错误的(在数据损坏的情况下)。
因此,在Python(Python 3)中,我们有两种类型的东西,否则可能看起来相似;对于文本,我们有str,它知道它是文本;它知道它应该表示哪些字母,但不知道可能是哪些字节,因为字母不是字节。我们还有bytestring,它不知道它是文本还是图像或任何其他类型的数据。
这两种类型表面上是相似的,因为它们都是事物的序列,但它们是序列的事物是完全不同的。
在实现上,str作为UCS-?存储在内存中,其中?是实现定义的,它可能是UCS-4,UCS-2或UCS-1,这取决于编译时选项以及所表示的字符串中存在哪些code points
“但是为什么”
一些看起来像文本的东西实际上是用其他术语定义的。这方面的一个很好的例子是世界上许多互联网协议。例如,HTTP是一个“文本”协议,实际上是使用RFC中常见的ABNF语法定义的。这些协议是用八位字节表示的,而不是字符,尽管也可以建议非正式的编码:
2.3.终值
规则解析为一串终端值,有时称为字符。在ABNF中,字符只是一个非负整数。在某些上下文中,将指定值到字符集(如ASCII)的特定Map(编码)。
这种区别很重要,因为不可能通过互联网发送文本,你唯一能做的就是发送字节。说“文本但使用‘foo’编码”会使格式变得复杂得多,因为客户端和服务器现在需要以某种方式自己解决编码业务,希望以同样的方式因为它们最终必须以字节的形式传递数据。这是双重无用的,因为这些协议很少涉及文本处理,服务器所有者和最终用户都不会对阅读单词Transfer-Encoding: chunked感兴趣,只要服务器和浏览器都正确理解它。
相比之下,在处理文本时,你并不真正关心它是如何编码的。你可以用任何你喜欢的方式来表达“Heävy Mëtal Ümlaüts”,除了“Heδvy Mλtal άmlaόts”
因此,不同的类型为您提供了一种方法,可以说“这个值”意味着“文本”或“字节”。

lkaoscv7

lkaoscv72#

Python * 不 * 知道如何表示字节串。这就是问题所在。
当你把一个值为97的字符输出到几乎所有的输出窗口时,你会得到字符'a',但这不是实现的一部分;这只是一个局部为真的东西。如果你想要一个编码,你不用bytestring。如果你用bytestring,你就没有编码。
你关于.txt文件的文章表明你误解了正在发生的事情。你看,纯文本文件也没有编码。它们只是一系列字节。这些字节被文本编辑器翻译成字母,但如果你偏离了常见的ASCII字符集,就不能保证其他人打开你的文件会看到和你一样的东西。

68bkxrlz

68bkxrlz3#

顾名思义,Python 3的bytestring(或Python 2.7中的str)是一个 * 字节 * 的字符串。而且,正如其他人所指出的,它是不可变的。
它不同于Python 3的str(或者更具体地说,Python 2.7中的unicode),后者是一个 abstractUnicode字符串(也称为UTF-32,尽管Python 3在后台添加了花哨的压缩以减少类似于UTF-8的实际内存占用,甚至可能是更通用的方式)。
基本上有三种方式来“解释”这些字节。你可以查看一个元素的数值,如下所示:

>>> ord(b'Hello'[0])  # Python 2.7 str
72
>>> b'Hello'[0]  # Python 3 bytestring
72

或者你可以告诉Python将一个或多个元素以8位字符的形式发送到终端(或文件,设备,套接字等)*,就像这样:

>>> print b'Hello'[0] # Python 2.7 str
H
>>> import sys
>>> sys.stdout.buffer.write(b'Hello'[0:1]) and None; print() # Python 3 bytestring
H

正如Jack所暗示的,在后一种情况下,是 * 你的终端 * 解释字符,而不是Python。
最后,正如你在自己的研究中所看到的,你也可以让 Python 来解释一个bytestring。例如,你可以在Python 2.7中构造一个抽象的unicode对象,如下所示:

>>> u1234 = unicode(b'\xe1\x88\xb4', 'utf-8')
>>> print u1234.encode('utf-8') # if terminal supports UTF-8
ሴ
>>> u1234
u'\u1234'
>>> print ('%04x' % ord(u1234))
1234
>>> type(u1234)
<type 'unicode'>
>>> len(u1234)
1
>>>

或者在Python 3中这样:

>>> u1234 = str(b'\xe1\x88\xb4', 'utf-8')
>>> print (u1234) # if terminal supports UTF-8 AND python auto-infers
ሴ
>>> u1234.encode('unicode-escape')
b'\\u1234'
>>> print ('%04x' % ord(u1234))
1234
>>> type(u1234)
<class 'str'>
>>> len(u1234)
1

(and我确信Python 2.7和Python 3之间围绕bystestring、strings和Unicode的大量语法混乱与Python 2.7的持续流行有关。我想当Python 3发明时,他们还没有意识到一切都将变成UTF-8,因此所有关于抽象的大惊小怪都是不必要的)。
但是如果你不想的话,Unicode抽象不会自动发生。bytestring的要点是你可以直接获取字节。即使你的字符串碰巧是UTF-8序列,你仍然可以访问序列中的字节:

>>> len(b'\xe1\x88\xb4')
3
>>> b'\xe1\x88\xb4'[0]
'\xe1'

这在Python 2.7和Python 3中都可以工作,区别在于Python 2.7中有str,而在Python 3中有bytestring
您还可以使用bytestring s做其他奇妙的事情,例如知道它们是否适合文件中的保留空间,直接通过套接字发送它们,正确计算HTTP content-length字段,以及避免Python Bug 8260。简而言之,当您的数据以字节进行处理和存储时,请使用bytestring s。

btqmn9zl

btqmn9zl4#

字节对象是不可变的单字节序列。文档对它们是什么以及如何使用它们有非常好的解释。

相关问题