【发布时间】:2012-11-08 07:22:02
【问题描述】:
假设我在 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()时,会收集每个字符对应的码点(编码名对应的码点)并放入数组或字节中。当我们执行decode()时究竟会发生什么? - 我们可以看到在 utf-16 和 utf-32 中都附加了一个 BOM,但是为什么在 utf-32 编码中附加了两个零字节呢?
【问题讨论】:
-
两个零字节是 utf32 BOM 的一部分(
0x0 0x0 0xff 0xfe是 big-endian 而不是 little-endian utf82 的 BOM)。 -
更多信息可以在spec中找到。基本上,在 Python 中编码成一串字节(原始数据)。
标签: python unicode encoding python-3.x