假设我在 Python 中有一个字符串:
>>> s = 'python'
>>> len(s)
6
现在我encode
这个字符串是这样的:
>>> b = s.encode('utf-8')
>>> b16 = s.encode('utf-16')
>>> b32 = s.encode('utf-32')
我从上面的操作中得到的是一个字节数组——即b
,b16
并且b32
只是字节数组(每个字节当然是 8 位长)。
但是我们对字符串进行了编码。那么这是什么意思?我们如何将“编码”的概念与原始字节数组联系起来?
答案在于这些字节数组中的每一个都是以特定方式生成的。让我们看看这些数组:
>>> [hex(x) for x in b]
['0x70', '0x79', '0x74', '0x68', '0x6f', '0x6e']
>>> len(b)
6
这个数组表示对于每个字符我们都有一个字节(因为所有字符都低于 127)。因此,我们可以说将字符串“编码”为 'utf-8' 会收集每个字符对应的代码点并将其放入数组中。如果代码点不能容纳在一个字节中,则 utf-8 会占用两个字节。因此 utf-8 消耗尽可能少的字节数。
>>> [hex(x) for x in b16]
['0xff', '0xfe', '0x70', '0x0', '0x79', '0x0', '0x74', '0x0', '0x68', '0x0', '0x6f', '0x0', '0x6e', '0x0']
>>> len(b16)
14 # (2 + 6*2)
在这里我们可以看到“编码为 utf-16”首先将两个字节的 BOM ( FF FE
) 放入字节数组中,然后对于每个字符将两个字节放入数组中。(在我们的例子中,第二个字节总是零)
>>> [hex(x) for x in b32]
['0xff', '0xfe', '0x0', '0x0', '0x70', '0x0', '0x0', '0x0', '0x79', '0x0', '0x0', '0x0', '0x74', '0x0', '0x0', '0x0', '0x68', '0x0', '0x0', '0x0', '0x6f', '0x0', '0x0', '0x0', '0x6e', '0x0', '0x0', '0x0']
>>> len(b32)
28 # (2+ 6*4 + 2)
在“以 utf-32 编码”的情况下,我们首先放入 BOM,然后为每个字符放入四个字节,最后将两个零字节放入数组中。
因此,我们可以说“编码过程”为字符串中的每个字符收集 1 2 或 4 个字节(取决于编码名称),并在它们前面添加和附加更多字节以创建最终的字节结果数组。
现在,我的问题:
- 我对编码过程的理解是正确的还是我遗漏了什么?
- 我们可以看到变量的内存表示,实际上
b
是一个字节列表。字符串的内存表示是什么?究竟是什么存储在内存中的字符串?b16
b32
- 我们知道,当我们做一个 时
encode()
,会收集每个字符对应的代码点(与编码名称对应的代码点)并放入一个数组或字节中。当我们执行 a 时究竟会发生什么decode()
? - 我们可以看到在 utf-16 和 utf-32 中都附加了一个 BOM,但是为什么在 utf-32 编码中附加了两个零字节呢?