Unicode 与 UTF-8

Unicode 是抽象的数字编码,UTF-8 是具体的字节编码。

第一个字节 第二个字节 第三个字节 第四个字节 用于实际编码的 bit 数量 能表示的最大值
- - - - - -
0xxxxxxx       7 127
110xxxxx 10xxxxxx     11 2047
1110xxxx 10xxxxxx 10xxxxxx   16 65535
11110xxx 10xxxxxx 10xxxxxx 10xxxxxx 21 1114111

Python 中,可以用 \u\U 前缀表示 Unicode 字符, 如 '\U0010ffff', '\uffff', '\U00000042', '\u0042'

emoji 例子 😋

Python:

>>> s = '😋'
>>> s.encode()
b'\xf0\x9f\x98\x8b'
>>> import json
>>> json.dumps(s)
'"\\ud83d\\ude0b"'
>>> ord(s)
128523
>>> len(s)
1

一个字符,在内存中,普通 ascii 字符占用 1 字节,普通汉字占用 2 字节,emoji 字符占用 4 字节,虽然他们都是长度 1。

一个字符串包含多种字符时,占用内存为 最占内存的字符 x 长度,"s" * 1024 占 1K 内存,"s" * 1023 + "😋" 占 4k 内存。

JavaScript:

> const s = '😋'
> s.length
2
> (s + 'x').length
3
> (s + '中').length
3
> new TextEncoder().encode(s)
Uint8Array [ 240, 159, 152, 139 ]
> new TextEncoder().encode('中')
Uint8Array [ 228, 184, 173 ]

> const n = 1024*1024*100
> const ss = '😋'.repeat(n)
> ss.indexOf('x')  // active this string
-1
// memory usage 400M

> const ss = '中'.repeat(n); ss.indexOf('x')
// memory usage 200M

> const ss = 's'.repeat(n); ss.indexOf('x')
// memory usage 100M

> const ss = 's'.repeat(n) + '中'.repeat(n); ss.indexOf('x')
// memory usage 400M, is 200M + 200M, not 100M + 200M

> const ss = 's'.repeat(n) + '😋'.repeat(n);; ss.indexOf('x')
// memory usage 600M, is 200M + 2 * 200M

JavaScript 的内存使用情况与 Python 类似,有对普通纯 ascii 字符串做优化。 emoji.length == 2,因为字符在内存中使用 UTF-16 编码存放,.length 返回的是 UTF-16 的单元数量。emoji 被认为是 2 单元占 4 字节内存。

Python 无法存储非法的 unicode:

>>> bytes([254,255]).decode()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
UnicodeDecodeError: 'utf-8' codec can't decode byte 0xfe in position 0: invalid start byte