JavaScript 的 String.length 到 UTF-16

气死了,之前更详细一点的刚写完,没保存,不小心弄没了,搞不回来的那种。于是有了下面的这一概念罗列版。。。

现象

const str = '😷'
const str1 = str.split('').reverse().join('')

console.log(str.length) // 2
console.log(str1) // 乱码

原理探索

术语和概念

  • 字符集:一堆字符组成的集合。
  • 字符编码集:与字符集形成一一映射关系的指定集合,比如一个由一堆比特序列组成的集合,与每个字符对应的那个比特序列可称为抽象编码。
  • Unicode:计算机科学领域里对世界上大部分文字进行整理、编码的一项业界标准。它定义了一套字符编码集,里面最多可容纳 17 * 2 ^ 16 = 1114112 个字符。
  • 码位:Unicode 字符编码集里的抽象编码。
  • 字符编码集的实现:将抽象编码进一步映射成一个以某一特定比特长度为单位的序列。
  • 码元:上述特定比特长度代表的那个东西就是码元。
  • 平面:Unicode 字符分为 17 组编排,每一组称为一个平面。
  • 基本平面:编码空间范围为 U+0000 ~ U+D7FF 和 U+E000 ~ U+FFFF 对应的那个平面。不连续的地方分成 U+D800 ~ U+DBFF 和 U+DC00 ~ U+DFFF 两部分,不对应任何字符。
  • 辅助平面:编码空间范围为 U+010000 ~ U+10FFFF 的那些平面,共 16 个。
  • UTF-16 是一种 Unicode 字符编码集的实现方式,它将基本平面里的字符编码为 1 个码元,其余平面编码成 2 个(包含一个前导代理和一个后尾代理),一个码元为 16 比特,所以它最后的编码是不定长的。
  • 前导代理:对应码位范围为 0xD800 ~ 0xDBFF(55296 ~ 56319)的码元,共 2 * 10 = 2048 个。
  • 后尾代理:对应码位范围为 0xDC00 ~ 0xDFFF(56320 ~ 57343)的码元,共 2 * 10 = 2048 个。
  • UTF-16 的编码算法:对于任意一给定码位
    1. 如果这个码位属于基本平面,返回码位对应的 16 比特的序列。
    2. 将码位减去 0x10000 得到一个属于 0x00000 ~ 0xFFFFF 内的 20 比特的序列,将高位的 10 比特(范围为 0x0000 ~ 0x03FF)加上 0xD800 得到前导代理(范围为 0x0000 ~ 0x03FF),低位的 10 比特(范围为 0xD800 ~ 0xDBFF)加上 0xDC00 得到后尾代理(范围为 0xDC00 ~ 0xDFFF),返回前导代理和后尾代理拼接成的 32 比特的序列,序列中依然是前导代理居高位,后尾代理居低位。

所以依次算法可知,对于任意一字符串,在 UTF-16 编码后生成的那串码元序列中,对任意一个码元进行判断就可以确定这个码元所对应的字符的边界,因为这个码元只有如下三种情况:

  • 为前导代理
  • 为后尾代理
  • 对应基本平面里的某个字符

解释

😷应该是一个在辅助平面的字符,它被 UTF-16 编码成一个由两个码元组成的序列,第一个码元是前导代理,第二个是后尾代理:

console.log('😷'[0].charCodeAt()) // 55357
console.log('😷'[1].charCodeAt()) // 56887

参考:

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。