BMP (基本多文种平面)
Unicode 中最初的 65,536 个码位 (U+0000 至 U+FFFF) 所在的区域。日常使用的绝大多数字符都收录于此,超出该范围的字符需要通过代理对来表示。
BMP (Basic Multilingual Plane,基本多文种平面) 是 Unicode 17 个"平面" (plane) 中的第一个 (Plane 0)。它包含从 U+0000 到 U+FFFF 的 65,536 个码位,收录了 ASCII、拉丁字母、希腊字母、西里尔字母、阿拉伯字母、日文假名以及 CJK 统一汉字的基本集 (约 20,000 字) 等世界主要文字体系的绝大部分字符。
BMP 之外还有 16 个辅助平面 (Supplementary Planes)。Plane 1 (SMP,辅助多文种平面) 包含表情符号、古代文字、音乐符号等。Plane 2 (SIP,辅助表意文字平面) 收录了 CJK 统一汉字扩展 B 及之后的汉字。这些辅助平面的字符码位在 U+10000 以上。
BMP 与辅助平面的区别在使用 UTF-16 编码的环境中会影响字符计数。UTF-16 中 BMP 字符用 2 字节 (1 个码元) 表示,而辅助平面字符需要 4 字节 (2 个码元 = 代理对)。JavaScript 的 String.length 返回的是码元数,因此包含表情符号 (辅助平面) 的字符串会返回比视觉字符数更大的值。
来看具体的例子。"A" (U+0041,BMP 内) 的 length 为 1。"𠮷" (U+20BB7,CJK 统一汉字扩展 B) 是 BMP 外的字符,length 为 2。"😀" (U+1F600,表情符号) 也在 BMP 外,length 为 2。而"👨👩👧👦" (家庭表情) 由 7 个码位组成 (其中 4 个在 BMP 外),length 为 11。
要准确计数字符,需要使用不区分 BMP 内外的方法。在 JavaScript 中,可以用 Array.from(str).length 或 [...str].length 获取码位级别的字符数。如果需要更精确的结果,使用 Intl.Segmenter 按书写素簇为单位计数,就能将组合字符和表情序列正确地计为 1 个字符。
BMP 内部也有一些特殊区域。U+D800 至 U+DFFF 是为代理对保留的,不能单独表示有效字符。U+E000 至 U+F8FF 是私用区 (Private Use Area),供字体厂商或企业分配自定义字符。U+FDD0 至 U+FDEF 以及每个平面末尾的 2 个码位是"非字符" (noncharacter),永远不会被分配给任何字符。