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
!为什么会这样?
4条答案
按热度按时间dced5bon1#
一个常见的误解是文本是ASCII或UTF-8或Windows-1252,因此字节是文本。
文本只是文本,就像图像只是图像一样。将文本或图像存储到磁盘的问题是将数据编码为字节序列的问题。有很多方法可以将图像编码为字节:JPEG、PNG、SVG,以及许多编码文本、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”
因此,不同的类型为您提供了一种方法,可以说“这个值”意味着“文本”或“字节”。
lkaoscv72#
Python * 不 * 知道如何表示字节串。这就是问题所在。
当你把一个值为97的字符输出到几乎所有的输出窗口时,你会得到字符'a',但这不是实现的一部分;这只是一个局部为真的东西。如果你想要一个编码,你不用bytestring。如果你用bytestring,你就没有编码。
你关于.txt文件的文章表明你误解了正在发生的事情。你看,纯文本文件也没有编码。它们只是一系列字节。这些字节被文本编辑器翻译成字母,但如果你偏离了常见的ASCII字符集,就不能保证其他人打开你的文件会看到和你一样的东西。
68bkxrlz3#
顾名思义,Python 3的
bytestring
(或Python 2.7中的str
)是一个 * 字节 * 的字符串。而且,正如其他人所指出的,它是不可变的。它不同于Python 3的
str
(或者更具体地说,Python 2.7中的unicode
),后者是一个 abstractUnicode字符串(也称为UTF-32,尽管Python 3在后台添加了花哨的压缩以减少类似于UTF-8的实际内存占用,甚至可能是更通用的方式)。基本上有三种方式来“解释”这些字节。你可以查看一个元素的数值,如下所示:
或者你可以告诉Python将一个或多个元素以8位字符的形式发送到终端(或文件,设备,套接字等)*,就像这样:
正如Jack所暗示的,在后一种情况下,是 * 你的终端 * 解释字符,而不是Python。
最后,正如你在自己的研究中所看到的,你也可以让 Python 来解释一个
bytestring
。例如,你可以在Python 2.7中构造一个抽象的unicode
对象,如下所示:或者在Python 3中这样:
(and我确信Python 2.7和Python 3之间围绕bystestring、strings和Unicode的大量语法混乱与Python 2.7的持续流行有关。我想当Python 3发明时,他们还没有意识到一切都将变成UTF-8,因此所有关于抽象的大惊小怪都是不必要的)。
但是如果你不想的话,Unicode抽象不会自动发生。
bytestring
的要点是你可以直接获取字节。即使你的字符串碰巧是UTF-8序列,你仍然可以访问序列中的字节:这在Python 2.7和Python 3中都可以工作,区别在于Python 2.7中有
str
,而在Python 3中有bytestring
。您还可以使用
bytestring
s做其他奇妙的事情,例如知道它们是否适合文件中的保留空间,直接通过套接字发送它们,正确计算HTTPcontent-length
字段,以及避免Python Bug 8260。简而言之,当您的数据以字节进行处理和存储时,请使用bytestring
s。btqmn9zl4#
字节对象是不可变的单字节序列。文档对它们是什么以及如何使用它们有非常好的解释。