【javascript学习指南】JavaScript Unicode 字符编码详解教程

更新时间:2019-10-24    来源:网页配色    手机版     字体:

【www.bbyears.com--网页配色】

Unicode 是为了解决传统的字符编码方案的局限而产生的,例如ISO 8859所定义的字符虽然在不同的国家中广泛地使用,可是在不同国家间却经常出现不兼容的情况。很多传统的编码方式都有一个共同的问题,即容许电脑处理双语环境(通常使用拉丁字母以及其本地语言),但却无法同时支持多语言环境(指可同时处理多种语言混合的情况)。

一、Unicode是什么?

Unicode源于一个很简单的想法:将全世界所有的字符包含在一个集合里,计算机只要支持这一个字符集,就能显示所有的字符,再也不会有乱码了。




它从0开始,为每个符号指定一个编号,这叫做"码点"(code point)。比如,码点0的符号就是null(表示所有二进制位都是0)。

U+0000 = null

上式中,U+表示紧跟在后面的十六进制数是Unicode的码点。




目前,Unicode的最新版本是7.0版,一共收入了109449个符号,其中的中日韩文字为74500个。可以近似认为,全世界现有的符号当中,三分之二以上来自东亚文字。比如,中文"好"的码点是十六进制的597D。

U+597D = 好

这么多符号,Unicode不是一次性定义的,而是分区定义。每个区可以存放65536个(216)字符,称为一个平面(plane)。目前,一共有17个(25)平面,也就是说,整个Unicode字符集的大小现在是221。

最前面的65536个字符位,称为基本平面(缩写BMP),它的码点范围是从0一直到216-1,写成16进制就是从U+0000到U+FFFF。所有最常见的字符都放在这个平面,这是Unicode最先定义和公布的一个平面。

剩下的字符都放在辅助平面(缩写SMP),码点范围从U+010000一直到U+10FFFF。




二、UTF-32与UTF-8

Unicode只规定了每个字符的码点,到底用什么样的字节序表示这个码点,就涉及到编码方法。

最直观的编码方法是,每个码点使用四个字节表示,字节内容一一对应码点。这种编码方法就叫做UTF-32。比如,码点0就用四个字节的0表示,码点597D就在前面加两个字节的0。

U+0000 = 0x0000 0000

U+597D = 0x0000 597D




UTF-32的优点在于,转换规则简单直观,查找效率高。缺点在于浪费空间,同样内容的英语文本,它会比ASCII编码大四倍。这个缺点很致命,导致实际上没有人使用这种编码方法,HTML 5标准就明文规定,网页不得编码成UTF-32。




人们真正需要的是一种节省空间的编码方法,这导致了UTF-8的诞生。UTF-8是一种变长的编码方法,字符长度从1个字节到4个字节不等。越是常用的字符,字节越短,最前面的128个字符,只使用1个字节表示,与ASCII码完全相同。

编号范围字节

0x0000 - 0x007F  1

0x0080 - 0x07FF  2

0x0800 - 0xFFFF  3

0x010000 - 0x10FFFF  4

由于UTF-8这种节省空间的特性,导致它成为互联网上最常见的网页编码。不过,它跟今天的主题关系不大,我就不深入了,具体的转码方法,可以参考我多年前写的《字符编码笔记》。

三、UTF-16简介

UTF-16编码介于UTF-32与UTF-8之间,同时结合了定长和变长两种编码方法的特点。

它的编码规则很简单:基本平面的字符占用2个字节,辅助平面的字符占用4个字节。也就是说,UTF-16的编码长度要么是2个字节(U+0000到U+FFFF),要么是4个字节(U+010000到U+10FFFF)。




于是就有一个问题,当我们遇到两个字节,怎么看出它本身是一个字符,还是需要跟其他两个字节放在一起解读?

说来很巧妙,我也不知道是不是故意的设计,在基本平面内,从U+D800到U+DFFF是一个空段,即这些码点不对应任何字符。因此,这个空段可以用来映射辅助平面的字符。

具体来说,辅助平面的字符位共有220个,也就是说,对应这些字符至少需要20个二进制位。UTF-16将这20位拆成两半,前10位映射在U+D800到U+DBFF(空间大小210),称为高位(H),后10位映射在U+DC00到U+DFFF(空间大小210),称为低位(L)。这意味着,一个辅助平面的字符,被拆成两个基本平面的字符表示。




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

四、UTF-16的转码公式

Unicode码点转成UTF-16的时候,首先区分这是基本平面字符,还是辅助平面字符。如果是前者,直接将码点转为对应的十六进制形式,长度为两字节。

U+597D = 0x597D

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

H = Math.floor((c-0x10000) / 0x400)+0xD800

L = (c - 0x10000) % 0x400 + 0xDC00




以字符为例,它是一个辅助平面字符,码点为U+1D306,将其转为UTF-16的计算过程如下。

H = Math.floor((0x1D306-0x10000)/0x400)+0xD800 = 0xD834

L = (0x1D306-0x10000) % 0x400+0xDC00 = 0xDF06

所以,字符的UTF-16编码就是0xD834 DF06,长度为四个字节。




五、JavaScript使用哪一种编码?




JavaScript语言采用Unicode字符集,但是只支持一种编码方法。

这种编码既不是UTF-16,也不是UTF-8,更不是UTF-32。上面那些编码方法,JavaScript都不用。

JavaScript用的是UCS-2!




六、UCS-2编码

怎么突然杀出一个UCS-2?这就需要讲一点历史。

互联网还没出现的年代,曾经有两个团队,不约而同想搞统一字符集。一个是1988年成立的Unicode团队,另一个是1989年成立的UCS团队。等到他们发现了对方的存在,很快就达成一致:世界上不需要两套统一字符集。

1991年10月,两个团队决定合并字符集。也就是说,从今以后只发布一套字符集,就是Unicode,并且修订此前发布的字符集,UCS的码点将与Unicode完全一致。




UCS的开发进度快于Unicode,1990年就公布了第一套编码方法UCS-2,使用2个字节表示已经有码点的字符。(那个时候只有一个平面,就是基本平面,所以2个字节就够用了。)UTF-16编码迟至1996年7月才公布,明确宣布是UCS-2的超集,即基本平面字符沿用UCS-2编码,辅助平面字符定义了4个字节的表示方法。

两者的关系简单说,就是UTF-16取代了UCS-2,或者说UCS-2整合进了UTF-16。所以,现在只有UTF-16,没有UCS-2。

七、JavaScript的诞生背景

那么,为什么JavaScript不选择更高级的UTF-16,而用了已经被淘汰的UCS-2呢?

答案很简单:非不想也,是不能也。因为在JavaScript语言出现的时候,还没有UTF-16编码。

1995年5月,Brendan Eich用了10天设计了JavaScript语言;10月,第一个解释引擎问世;次年11月,Netscape正式向ECMA提交语言标准(整个过程详见《JavaScript诞生记》)。对比UTF-16的发布时间(1996年7月),就会明白Netscape公司那时没有其他选择,只有UCS-2一种编码方法可用!




八、JavaScript字符函数的局限

由于JavaScript只能处理UCS-2编码,造成所有字符在这门语言中都是2个字节,如果是4个字节的字符,会当作两个双字节的字符处理。JavaScript的字符函数都受到这一点的影响,无法返回正确结果。




还是以字符为例,它的UTF-16编码是4个字节的0xD834 DF06。问题就来了,4个字节的编码不属于UCS-2,JavaScript不认识,只会把它看作单独的两个字符U+D834和U+DF06。前面说过,这两个码点是空的,所以JavaScript会认为是两个空字符组成的字符串!




上面代码表示,JavaScript认为字符的长度是2,取到的第一个字符是空字符,取到的第一个字符的码点是0xDB34。这些结果都不正确!




解决这个问题,必须对码点做一个判断,然后手动调整。下面是正确的遍历字符串的写法。

while (++index < length) {

// ...

if (charCode >= 0xD800 && charCode <= 0xDBFF) {

output.push(character + string.charAt(++index));

} else {

output.push(character);

}

}

上面代码表示,遍历字符串的时候,必须对码点做一个判断,只要落在0xD800到0xDBFF的区间,就要连同后面2个字节一起读取。

类似的问题存在于所有的JavaScript字符操作函数。

String.prototype.replace()

String.prototype.substring()

String.prototype.slice()

...

上面的函数都只对2字节的码点有效。要正确处理4字节的码点,就必须逐一部署自己的版本,判断一下当前字符的码点范围。

九、ECMAScript 6




JavaScript的下一个版本ECMAScript 6(简称ES6),大幅增强了Unicode支持,基本上解决了这个问题。

(1)正确识别字符

ES6可以自动识别4字节的码点。因此,遍历字符串就简单多了。

for (let s of string ) {

// ...

}

但是,为了保持兼容,length属性还是原来的行为方式。为了得到字符串的正确长度,可以用下面的方式。

Array.from(string).length

(2)码点表示法

JavaScript允许直接用码点表示Unicode字符,写法是"反斜杠+u+码点"。

'好' === '\u597D' // true

但是,这种表示法对4字节的码点无效。ES6修正了这个问题,只要将码点放在大括号内,就能正确识别。




(3)字符串处理函数

ES6新增了几个专门处理4字节码点的函数。

String.fromCodePoint():从Unicode码点返回对应字符

String.prototype.codePointAt():从字符返回对应的码点

String.prototype.at():返回字符串给定位置的字符

(4)正则表达式

ES6提供了u修饰符,对正则表达式添加4字节码点的支持。




(5)Unicode正规化

有些字符除了字母以外,还有附加符号。比如,汉语拼音的Ǒ,字母上面的声调就是附加符号。对于许多欧洲语言来说,声调符号是非常重要的。




Unicode提供了两种表示方法。一种是带附加符号的单个字符,即一个码点表示一个字符,比如Ǒ的码点是U+01D1;另一种是将附加符号单独作为一个码点,与主体字符复合显示,即两个码点表示一个字符,比如Ǒ可以写成O(U+004F) + ˇ(U+030C)。

// 方法一

'\u01D1'

// 'Ǒ'

// 方法二

'\u004F\u030C'

// 'Ǒ'

这两种表示方法,视觉和语义都完全一样,理应作为等同情况处理。但是,JavaScript无法辨别。

'\u01D1'==='\u004F\u030C'

//false

ES6提供了normalize方法,允许"Unicode正规化",即将两种方法转为同样的序列。

'\u01D1'.normalize() === '\u004F\u030C'.normalize()

// true

关于ES6的更多介绍,请看《ECMAScript 6入门》。

==========================




字符编码 unicode 及其在javascript 中的使用

一、javascript 使用 unicode16 字符集,可以使用中文变量名和函数名

计算机使用 8 位(bit)二进制表示一个字节(Byte),计算机内存最小寻址单位就是 1 字节。
早期为了在计算机上使用同一的方式使用字符,使用无符号整数来标记字符。
ANSI(美国国家标准局)制订了ASCII(American Standard Code for Information Interchange,美国信息交换标准代码),使用一个字节大小的二进制数来编码每个字符。
ASCII已经被国际标准化组织(ISO)定为国际标准,称为ISO 646标准。
一个字节为8位二进制,2的8次方为256,因此有256个字符可以用一个字节来表示(0~255),
但ASCII字符集只设计了128个字符(字母、数字、一些标点符号和控制字符),因此实际上只用到7位二进制,第八位设置为0,剩下的128个编码位置是闲置的。
有的计算机厂商可能会利用闲置的128个空位来制订一些字符的编码,称为OEM字符集。
例如,IBM使用多出来的128位扩展了一个ASCII 扩展表,包含了一些控制符和制表符等等,被广泛使用在电子元件的数据通讯和存储中,但OEM字符集不是通用的标准。

为了编码更多的字符,2个研究字符编码的机构合并研究成果,制订了 unicode 字符集。
unicode 字符集使用使用多个字节来为字符编码,按使用的字节数不同制订了不同方案,所有 unicode 编码方案前 1 个字节(256个码位)的编码对应的字符都是 ASCII 字符集中的字符。
目前 unicode 编码已经达到 64 位,使用 8 个字节标记一个字符。
如果每个字符用2个字节(16位二进制数)来标记,可以编码 65536 个字符(2 的16次方),这基本上已经可以标记世界上所有国家的语言符号,因此,在实际中通用的是UCS-2通用字符集(Universal Character Set,UCS),由ISO制定的ISO 10646(或称ISO/IEC 10646)标准所定义,UCS 为第一字节的128个空位增补了一个字符集,称为 C1控制符及拉丁文补充-1 (C1 Control and Latin 1 Supplement)。

UCS-2字符集编码法有17个位面,每个位面都用2个字节来标记字符,17个位面可以映射 1,112,064个字符,其中最常用最重要的是编号为 0 的位面,里面包含了最常用的字符编码,称为基本多国语言平面BMP(Basic Multilingual Plane)。
unicode 第 0 平面(BMP)中的编码被划分为不同区段,各国文字符号、控制符、制表符、图形字符等 都有连续的分布,
其中中文简繁体区段是 4E00-9FBF

4E00-9FBF:CJK 统一表意符号 (CJK Unified Ideographs)
0000-007F:C0控制符及基本拉丁文 (C0 Control and Basic Latin)  
0080-00FF:C1控制符及拉丁文补充-1 (C1 Control and Latin 1 Supplement)  
0100-017F:拉丁文扩展-A (Latin Extended-A)  
0180-024F:拉丁文扩展-B (Latin Extended-B)  
0250-02AF:国际音标扩展 (IPA Extensions)  
02B0-02FF:空白修饰字母 (Spacing Modifiers)  
0300-036F:结合用读音符号 (Combining Diacritics Marks)
(......还有很多国家语言和甲骨文等已经不用或极少使用的语言或字符的专用区段)

从第1位面开始,字符的unicode编码已经超出16位二进制数的范围,因此UCS-2无法使用2个字节直接编码BMP位面之外的字符。
但是,在 UCS-2 编码中,区段 UD800 到 UDFFF 的码位是闲置的保留位,
因此,可以使用这个区段中的码位通过一定的转换方式映射到其他位面的 unicode 编码。

在实际的字符传输和存储行为中,为了节省字节数,可能不会直接传输 unicode 编码,而是使用 Unicode转换格式(Unicode Transformation Format,简称为UTF),目前常见的 UTF格式有UTF-7, UTF-7.5, UTF-8, UTF-16, 以及 UTF-32,他们是由 ITTF(Internet Engineering Task Force,互联网工程任务组)组织进行标准化的,UTF-8 和 UTF-16 编码使用比较广泛。

UTF-16 编码:该编码法在 UCS-2 第0位面字符集的基础上利用 D800-DFFF 区段的码位通过一定转换方式将超出2字节的字符编码为一对16比特长的码元(即32bit,4Bytes),称作代理码对 (surrogate pair)。

例如字符'𪚥',他处于编号'2'的位面(总共17个位面,位面编号为16进制数0-10,第0位面可以舍去编号0直接用4位16进制数编码),码位是A6A5,即unicode编码为 2A6A5,它在UTF-16中的代理码对为 d869 dea5,但是通过 js 的charCodeAt()函数只能得出高位码对,但是这并不影响解码软件对字符编码进行定位,因为这些字符的代理码对都是成对地分布在  UD800-UDFFF 区段内的,并不存在交叉的现象,知道高位码对也可以简单地搜索到低位码对。
复制代码

('𪚥').charCodeAt(0).toString(16) //d869

document.write('\ud869\udea5'); // 𪚥

//我们可以通过UTF-8编码来推算出这个字符的 unicode 编码
parseInt(encodeURI('𪚥').split('%').slice(1).map(function(v){ return (Array(8).join(0)+parseInt(v,16).toString(2)).slice(-8) }).join().replace(/^1{2,}0|\,10/g,''),2).toString(16);// 2a6a5



UTF-16 编码出现以前,UD800-UDFFF 区段的码位可能会被一些计算机产品设计者利用,
而且其他位面的字符极少用到,因此,一些软件可能无法正确识别代理码对,这可能会导致一些BUG。例如,python 2.6 在 UNIX 平台上便无法正确识别代理码对。如果一个软件声称自己支持UCS-2,那么他很可能是不支持UTF-16的。


javascript 跟 java 一样使用UTF-16编码,因此, 实际上 javascript 程序中变量名和函数名可以使用ASCII 之外的字符,例如中文,不过网页文件保存的编码格式要注意,使用的编码格式对字符编码的范围应当不小于 UTF-16,比如保存为 utf-8 编码。

function 试试看(){
var 打个招呼 = {你好:'好你妹!'};
alert(打个招呼.你好);
}


试试看();



二、字符编码格式及其在 javascript 中的使用
1、unicode 16进制编码
unicode 16 使用 16位二进制编码字符,但是其编码格式在书面上使用16进制(二进制写起来太长了),在javascript中, \u 加 4个16进制字符表示一个字符的编码(每个字节 8 位二进制对应2位十六进制,2^8 = 256 = 16^2),不足4位16进制的,高位用0补足,比如 \u55B5 表示汉字 "喵",字母 "a" 的 ASCII 码是10进制 97,表示成 16 进制 unicode 编码格式就是 \u0061
试试打印出来:
document.write('\u55B5');
document.write('\u0061')

2、javascript charCodeAt 和 String.fromChartCode 使用 10 进制编码
在 javascript 字符串的 charCodeAt 和 String.fromChartCode 中取得和使用的字节编码都是 10 进制的,因此在 document.write 和 这些方法配合使用时需要进行进制转换。
另外要注意的是,如果一个变量保存了一个字符的 unicode() 编码,你想用 document.write() 打印到页面上就需要注意了,不要将'\u' 转义成 '\\u' ,如果转义了,需要使用 eval() 来执行,否则将直接把编码打印出来:

var code1 = '\\u0061';
document.write(code1); // \u0061
var code2 = '\u0061';
document.write(code2); // a

但是在表达式中,也许你想拼接出 unicode 编码后打印字符串,这就要注意了,因为在字符串中 \u 后面必须接 4个十六进制字符才是合法的语法,所以不得不转义:

var code = "\\u"+("0000"+('a').charCodeAt(0).toString(16)).slice(-4);
document.write(code); // \u0061
document.write(eval('"'+code+'"')); //正确做法 ,注意eval 时加上引号,因为 document.write 接受的参数是字符串,document.write("\u0061"),其中 \u0061 是单个字符,而不是可以分割的多个字符组成的字符串 (  "\\"+"u"+"0061" ), 而形如document.write(\u0061) 的语句是个语法错误.

 试试下面代码

function \u0061(){ console.log(123) }
\u0061();
a();

 

3、javascript 中的单字节编码
在 js 中,可以使用 \x 加 2位16进制字符标记一个单字节的字符,例如字符 'a' 可以表示为 \x61,用法类似\u 加4位16进制编码。
document.write('\x61');// a

\u 加四位十六进制数 或 \x 加2位十六进制数属于转义字符,在 js 字符串长度中只算 1 个,
转义字符不能直接用于 HTML 文件(不会转换后输出,而是直接输出转义格式的字符串),但可以用 document.write()打印出来,因为在 js 语法范围内,虽然表达方式不一样,但是转义字符和直接的字符字面量都是指同一个东西:

console.log("\u0061"==='a'); //true

 

('123|u55b5abc').length //12
('123|u55b5abc').split('') //["1", "2", "3", "|", "u", "5", "5", "b", "5", "a", "b", "c"]
('123\u55b5abc').length //7
('123\u55b5abc').split('') // ["1", "2", "3", "喵", "a", "b", "c"]



4、 xss 与 字符编码

当向页面显示用户输入的内容是,通常我们要过滤或转义 script 标签以避免 XSS 攻击,

但是,需要注意的是, \u+16进制 、\x+16进制 不能在html中自动转换(它们用于js程序),但是字符实体(它用于html)可以,如果出现在模板中的 HTML 标签属性中就需要特别注意了,他们会正确解码后才赋给标签的属性,例如:

document.body.innerHTML = '';


对标签属性之进行转义时,需要对注意,比较下面2个做法:

document.body.innerHTML = 'click me';

document.body.innerHTML = 'click me';

 
5、八进制转义字符

js 字符串中,\ 开始后接正数,可能被解析为8进制转义字符。

一个八进制转义字符形成的条件是:斜线后面接的整数最长3位,至少1位,单个数字的字面值不大于8。

如果过不满足任意一项,都将结束一个字符的解释,开始新字符的解释。八进制转义字符的10进制数字字面值最大值为377(即10进制的255),即 '\377' 被解析为一个字符,'\378' 被解析为2个字符: '\37' 和 '8'

'\377'.length   //1
console.log('\377')   // ÿ
'\378'.length //2
'\128'.length //2
 
console.log('\127') //W
'W'.charCodeAt(0) //87
(87).toString(8) //'127'

  
三、UTF-8 编码
由于 unicode 编码的前 128 位是ASCII 码,他们已经包含大部分西欧语言的字符,而且只要一个字节就可以全部编号了,还多出来 128 个空位,8位2进制,其实只使用了7位,第8位被设置为0,事实上,电子邮件使用的base64编码就认为第8位是1的的字符编码都是传输错误。
对于西欧语言来说,所有字符都使用 2个字节来标记太浪费计算机内存了,于是有了UTF-7、UTF-8 等变长编码方式,其中 UTF-8 又称万国码,使用最广泛,现在已经标准化为RFC 3629。

UTF-8的编码规则有二条:
1) 对于单字节的符号,字节的第一位设为0,后面7位为这个符号的unicode码。因此对于英语字母,UTF-8编码和ASCII码是相同的。
2) 对于n字节的符号(n>1),第一个字节的前n位都设为1,第n+1位设为0,后面字节的前两位一律设为10。剩下的没有提及的二进制位,全部为这个符号的unicode 二进制码,如果空位多于实际的unicode 二进制码位,在高位补0。
因为多字节字符编码要使用前置标记位,UFT-8 对汉字的编码可能会超出2个字节达到3个字节,更少用的字符可能达到4个字节的编码。

下表是UTF-8 编码的码位分布情况,其中的 x 表示字符的 unicode 二进制编码
Unicode符号范围 (十六进制) | UTF-8编码方式 | 二进制)
---------------------------------------------------------------------
0000 0000-0000 007F | 0xxxxxxx
0000 0080-0000 07FF | 110xxxxx 10xxxxxx
0000 0800-0000 FFFF | 1110xxxx 10xxxxxx 10xxxxxx
0001 0000-0010 FFFF | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx

例如,汉字 '喵' 的UTF-8编码十六进制格式是 E596B5,把他们转换二进制:
parseInt('E5',16).toString(2) // 11100101
parseInt('96',16).toString(2) // 10010110
parseInt('B5',16).toString(2) // 10110101
因此 它的 UTF-8 二进制编码为 11100101 10010110 10110101,
其中的 二进制 unicode 编码就是 01010101 10110101
转换为 16进制编码格式就是
parseInt('0101010110110101',2).toString(16) //55b5
因此它的 unicode-16 的16进制编码是 55b5 ,试打印出来
document.write('\u55b5'); // 喵

四、 URL 编码函数: escape(已经不推荐使用) 、encodeURI、encodeURIComponent
这些函数不会对 ASCII 字母和数字进行编码,但会对一些特殊符号和多字节字符进行16进制编码,其中 escape 使用unicode-16 编码,encodeURI、encodeURIComponent 使用 UTF-8编码。
他们对 单字节 ASCII 特殊符号编码结果都是一样的,格式是 % + 2位16进制编码,例如空格被编码为 %20
对于多字节字符,escape 的编码格式为 %u + unicode 十六进制双字节编码( 如果是单字节字符则是 % + unicode + 十六进制编码),而 encodeURI、encodeURIComponent 则是在字符的每个 UTF-8 编码字节前加 % ,例如 汉字 '喵' 的编码分别是:
escape('喵'); // %u55B5
encodeURI('喵'); // %E5%96%B5
encodeURIComponent('喵'); // %E5%96%B5

encodeURI、encodeURIComponent 的区别是:
encodeURI 不对在URL 上使用的特殊含义的字符拜年吗,如 ;/?:@&=+$,# , 所以你可以使用 encodeURI 对一个URL编码后仍能正确使用它访问网络资源或文件目录,
encodeURIComponent 的编码对象是 url 上的组件,例如附加参数,他会对 ;/?:@&=+$,# 这些用于分隔 URI 组件的标点符号 进行编码,因此不应该使用它对整个 URL 进行编码。

五、字符实体中的 unicode
HTML 字符实体可以直接用于HTML文件中,写法为 '&#' +unicode 编码的十进制数字 + ';'
或者 '&#x' +unicode 编码的十六进制数字 + ';'
如 大于号 > 的字符实体 为 > 或者 > 高位可以不补0,也可以写成 >
(62).toString(16) //3e
document.write('>'); // 打印出 大于号 >

下面是HTML中常用的字符实体表,其中只有少部分有专门用于 HTML 文件输出的实体名,任何 unicode-16 字符都可以通过字符实体在 HTML 文件中打印出来。
http://www.w3school.com.cn/tags/html_ref_entities.html

六、正则表达式中的 unicode 编码

var str = 'asd乃阿当123安迪asd123啊那的';
str.match(/[\u0000-\u00ff]/g); //单字节字符
//["a", "s", "d", "1", "2", "3", "a", "s", "d", "1", "2", "3"]

str.match(/[\x00-\xff]/g); //单字节字符
//["a", "s", "d", "1", "2", "3", "a", "s", "d", "1", "2", "3"]

str.match(/[\u0100-\uffff]/g); //双字节字符
//["乃", "阿", "当", "安", "迪", "啊", "那", "的"]


七、大端通讯、小端通讯和BOM(Byte Order Mark)
在数据通讯中传输数据时,可以采用两种数据传输方式:高位在前(大端,big endian)或高位在后(小端,little endian).
例如汉字'喵' 的unicode 16进制编码为 16进制数 0x55B5 ,传输方式:
大端通讯:0x55 0xB5
小端通讯:0xB5 0x55

BOM 是针对单个字符的本身的多字节编码而言的,如果字符的编码都是使用一个字节来编码则不存在字节序的问题,例如 ASCII 编码就不会存在字节序问题。
接收的二进制数据按每8位1字节,每字节使用2个十六进制字符编码为 unicode 后,存在一个问题,如何区别是高位在前还是高位在后。
Unicode规范中有一个字符的名字叫做”零宽度非换行空格“(ZERO WIDTH NO-BREAK SPACE),用FEFF表示。这正好是两个字节,而且,编码为FFFE 的字符在 unicode 中不存在,因此在存储文件或在网络传输字节流时,可以用这个字符来标记大端通讯和小端通讯。
UCS规范建议我们在传输字节流前,先传输字符"ZERO WIDTH NO-BREAK SPACE"。
在存储文件时,如果一个文本文件的头两个字节是FE FF,就表示该文件采用大端方式;如果头两个字节是FF FE,就表示该文件采用小端方式。

由于UTF-8 多字节编码方式定义其第一个字节的前几位使用 1 来声明该字符占多少个字节,因此,UTF-8编码方式不存在编码字节顺序的问题,但是仍然可以给 UTF-8 字节数据加上大端小端标记(带BOM 的UTF-8 编码格式)。 BOM 对于 UTF-8 格式文件是没实用意义的,因此一般推荐保存为不带 BOM 的UTF-8 格式。

javascript 的类型数组在读取二进制数据(BLOB)时,是以 小端方式存入数组的。

八、中文编码 GB2312 、GBK、BIG5
这些编码不是unicode 编码而是区位码和汉字内码,这些编码前一个字节仍然兼容ASCII,GB2312(简体) 、GBK(简繁体)由中国大陆机构编撰,BIG5(繁体)由港奥台相关机构和计算机厂商编撰,区位码在 windows 平台使用的代码页转换表(CodePage)映射到 unicode 编码。
代码页技术现在已经广泛为各种平台所采用。
UTF-7的代码页是65000,UTF-8的代码页是65001。

 


九、维基百科中关于 utf-16 代理码对算法的描述:
从U+10000到U+10FFFF的码位

辅助平面(Supplementary Planes)中的码位,在UTF-16中被编码为一对16比特长的码元(即32bit,4Bytes),称作 code units called a 代理对(surrogate pair), 具体方法是:
UTF-16解码 lead \ trail     DC00    DC01       …       DFFF
D800     10000     10001     …     103FF
D801     10400     10401     …     107FF
  ⋮     ⋮     ⋮     ⋱     ⋮
DBFF     10FC00     10FC01     …     10FFFF

    码位减去0x10000, 得到的值的范围为20比特长的0..0xFFFFF.
    高位的10比特的值(值的范围为0..0x3FF)被加上0xD800得到第一个码元或称作高位代理(high surrogate), 值的范围是0xD800..0xDBFF. 由于高位代理比低位代理的值要小,所以为了避免混淆使用,Unicode标准现在称高位代理为前导代理(lead surrogates).
    低位的10比特的值(值的范围也是0..0x3FF)被加上0xDC00得到第二个码元或称作低位代理(low surrogate), 现在值的范围是0xDC00..0xDFFF. 由于低位代理比高位代理的值要大,所以为了避免混淆使用,Unicode标准现在称低位代理为后尾代理(trail surrogates).

由于高位代理、低位代理、BMP中的有效字符的码位,三者互不重叠,搜索是简单的: 一个字符编码的一部分不可能与另一个字符编码的不同部分相重叠。这意味着UTF-16是自同步(self-synchronizing): 可以通过仅检查一个码元就可以判定给定字符的下一个字符的起始码元

 

计算过程:

//以为 字符 𪚥 为例,其 unicode 16进制编码 2A6A5,unicode 编码的二进制码位数:(0xFFFFF).toString(2).length==20
var c =  (Array(20).join(0)+parseInt(0x2A6A5-0x10000).toString(2)).slice(-20);
var c1 = c.slice(0,10);
var c2 = c.slice(10,20);
console.log(c,'|',c1,'|',c2);

var l = (parseInt(c1,2)+0xd800).toString(16);
var t = (parseInt(c2,2)+0xDC00).toString(16);
var code = '\\'+'u'+l+'\\'+'u'+t;

document.write(eval("'"+code+"'")); //

本文来源:http://www.bbyears.com/wangyezhizuo/75125.html