Unicode 与 JavaScript。

在上一篇我们讲解了 Unicode 和 UTF-8 的关系,今天,让我们来看看在 JavaScript 中 编码格式的应用。

Unicode 就是包含了所有的字符在一个集合内,计算机只要支持这一个字符集,就能显示所有的字符,再也不会有乱码了。Unicode 中的每一个字符都指定了一个编码,称为符号的“码点”。

U+0000   

U+ 紧跟在后面的十六进制数就是 Unicode 码点。

 Unicode 代码点总数1114111(0x10FFFF), 这么多的字符不是一次性定义的,而是分区定义。每个区可以存放65536个(2^16)字符,称为一个平面(plane),仅占可寻址 Unicode 代码点总数的 1/17。目前,一共有17个(2^5)平面,也就是说,整个Unicode字符集的大小现在是2^21。

最前面的65536个字符位,称为基本平面(缩写BMP), 它的码点范围是从0一直到2^16-1,写成16进制就是从U+0000到U+FFFF。它是Unicode 最先定义和公布的一个平面。剩下的字符都放在辅助平面(简称 SMP ),码点范围从 U+010000 到 U+10FFFF。

UTF-16 是介于 UTF-32 和 UTF-8的, UTF-32 的每个码点是使用四个字节,字节内容一一对应码点。UTF-8 是可变长的使用 1-4个字节不等。而UTF-16 是使用二个字节(U+0000到U+FFFF)和四个字节(U+010000到U+10FFFF)。

正常写的话,我们一个码点占两个字符,那么我们是把 遇到的两个字节看作一个字符,还是与后面的两个字节一起看作一个字符呢?我们得有一个识别的依据吧,UTF-8 会把一个字节的第一位是0还是1来进行不同的表示字节占有数。

UTF-16 是怎么表示的呢? 还记得前面说的平面嘛,在基本平面内,从 U+D800 到 U+DFFFF, 是一个空段,这些个码点不对应字符,感觉是故意这么设计的,反正非常的巧妙,我们可以利用这些空的码点进行一个映射,因为 辅助平面的字符位共有 2^20 个,而且空出来的正好是 2^20 个空白的码点,我们可以非常巧妙的利用到这空出的码点。将这20位拆成两半,前10位映射在U+D800到U+DBFF(空间大小2^10),称为高位(H),后10位映射在U+DC00到U+DFFF(空间大小210),称为低位(L)。这意味着,一个辅助平面的字符,被拆成两个基本平面的字符表示。

1048576个码点正好代表了 2^20个字符(可以自己算一次2^20 === 1048576)。在 0 ~ 65536 之间正好留出了 两个 2^10 个字符,也就是  U+D800 ~ U+DBFF,另一个是 U+DC00 ~ U+DFFF。我们使用映射的方式,将 2^20 分为两个 2^10 填补。就可以将辅助平面的字符通过基本字符表示出来。

2^20 = 1048576;  // 辅助平面的字符

1114111 - 1048576 = 65535; // 基础平面中的字符。

2^10 = 1024;  

DFFF - D800 = 1024 = 2^10;

DFFF - DC00 = 1024 = 2^10;

然后将补充字符 分成两半,前10位映射在U+D800到U+DBFF, 后10位映射在U+DC00到U+DFFF。

注意:不是说 2^10 + 2^10 = 2^20,而是以位来分。

所以,当我们遇到两个字节,发现它的码点在U+D800到U+DBFF之间,就可以断定,紧跟在后面的两个字节的码点,应该在U+DC00到U+DFFF之间,这四个字节必须放在一起解读。

我们怎么来进行 UTF-16 的转码呢?

首先,给定一个 Unicode 的码点,我们需要确定,它是属于基本平面的字符,还是在辅助平面的字符,如果是前面的字符,直接将码点转为对应的十六进制形式,长度为两个字节。

U+597D = 0x597D

如果是辅助平面的,我们就需要先映射,然后在转十六进制。

我们可以以中文中的 “汉” 为例, Unicode 的码点为 0x20BB7,很明显超出了基本平面的范围 (0x0000-0xFFFF),因此需要使用四个字节表示。那么如何转为 UTF-16呢?

首先得计算出来,它的超出的部分。 

0x20BB7 - 0x10000 计算超出的部分。

它是属于辅助平面的,所以是在 2^20内,所以我们用20个二进制来表示它。

结果为 0001000010 1110110111

分成两部分,前10位映射U+D800 - U+DBFF 之间.

U+D800 对应的二进制数为  1101100000000000,我们直接填充后面的 10 个 2进制即可。我们用  0001000010 来填充 1101100000000000 的 后十位. 得到 1101100001000010,转为16进制得到 0xD842.

低位的话,我们用同样的道理,得到 0xDFB7。因此得出汉子 的 UTF-16 编码为 0xD842 0xDFB7。

如果是辅助平面字符, Unicode 3.0 版本给出了转码公式

H = Math.floor((c - 0x10000) / 0x400) + 0xD800
高位H, 我们先计算超出基本字符的部分,然后/0x400(2^10), 
保留前面十位,加上U+D800 对应的二进制数为  1101100000000000,结果转为十六进制。

L = (c - 0x10000) % 0x400 + 0xDcC00
低位L, 我们先计算超出基本字符的部分,然后%0x400  (2^10), 
保留后十位,加上0xDC00 对应的二进制数为  1101110000000000,结果转为十六进制。

那么JavaScript 中使用的是哪种编码。

在 ES 5.1 中有一段话。

A conforming implementation of this International standard shall interpret characters in conformance with the Unicode Standard, Version 3.0 or later and ISO/IEC 10646-1 with either UCS-2 or UTF-16 as the adopted encoding form, implementation level 3. If the adopted ISO/IEC 10646-1 subset is not otherwise specified, it is presumed to be the BMP subset, collection 300. If the adopted encoding form is not otherwise specified, it is presumed to be the UTF-16 encoding form.

说明了 javaScript 引擎使用的编码格式。JS 引擎可以选择使用 UCS-2 或者 UTF-16。

那么为什么会选择 UTF-16呢? 由于历史原因,最先出现的 UCS-2,接着 1996 年 UTF-16 随着 Unicode 2.0 标准诞生,就可以迁移到了 UTF-16。但是它们无法移至 UTF-8,是因为这将破坏 API 接口中的二进制兼容性(以及其他功能)。

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值