码位
码位(code point,又称代码点)是 Unicode 为每一个字符分配的唯一整数编号,它标识的是"哪个字符",而不涉及字符在内存或文件里如何存储。Unicode 的码位空间从 U+0000 到 U+10FFFF,共约 111 万个位置,习惯用 U+ 加十六进制书写,例如 A 是 U+0041、中 是 U+4E2D、😀 是 U+1F600。
码位只是一个抽象数字,真正落到字节要经过编码。同一个码位在不同编码里占用的单元(码元)数量并不相同:
- UTF-8:一个码位用 1~4 个字节表示;
- UTF-16:基本平面用 1 个 16 位码元,增补平面用 2 个(代理对);
- UTF-32:固定用 1 个 32 位单元,数值直接等于码位。
常见误区是把码位、字节、"看到的字符"混为一谈。一个可见字符(字形簇)可能由多个码位组合而成,比如带重音的 é 或带肤色修饰的 emoji;因此字符串的码位个数既不等于视觉字符数,也不等于字节数。此外 U+D800~U+DFFF 是 UTF-16 的代理区,不对应真实字符,单独出现属于非法码位。想查某个字符对应的码位,可用站内 unicode-lookup、unicode 工具直接检索。
常见问题
码位和字节是一回事吗?
不是。码位是字符的抽象编号,字节是它经过某种编码后的存储形式。以 UTF-8 为例,同一个码位可能占用 1 到 4 个字节。
码位(code point)和码元(code unit)有什么区别?
码位是字符的编号,码元是某种具体编码方案里的最小存储单位。比如 UTF-16 的码元是 16 位,增补平面的一个码位需要两个码元(代理对)才能表示。
为什么 JavaScript 里字符串长度和实际字符数对不上?
因为 String.length 数的是 UTF-16 码元数,而不是码位数。超出基本平面的字符(如许多 emoji)由代理对表示,会被算成 2。可以用 [...str].length 或 Array.from 按码位来计数。