JavaScript基础教程之Unicode
沉沙 2018-07-10 来源 : 阅读 1151 评论 0

摘要:Unicode是目前绝大多数程序使用的字符编码,定义也很简单,用一个码点(code point)映射一个字符。希望阅读本篇文章以后大家有所收获,帮助大家对JavaScript的理解更加深入。

本文涉及知识点:

Unicode (BMP/SP)
UTF-8 UTF-16 UTF-32 UCS-2
javascript字符处理

Unicode

Unicode是目前绝大多数程序使用的字符编码,定义也很简单,用一个码点(code point)映射一个字符。码点值的范围是从U+0000到U+10FFFF,可以表示超过110万个符号。下面是一些符号与它们的码点

· A的码点 U+0041

· a的码点 U+0061

· ©的码点 U+00A9

· ☃的码点 U+2603

· 的码点 U+1F4A9

对于每个码点,Unicode还会配上一小段文字说明,可以在codepoints.net查到,比如 

Unicode最前面的65536个字符位,称为基本平面(BMP-—Basic Multilingual Plane),它的码点范围是从U+0000到U+FFFF。最常见的字符都放在这个平面,这是Unicode最先定义和公布的一个平面。
剩下的字符都放在补充平面(Supplementary Plane),码点范围从U+010000一直到U+10FFFF,共16个。

UTF与UCS

UTF(Unicode transformation format)Unicode转换格式,是服务于Unicode的,用于将一个Unicode码点转换为特定的字节序列。常见的UTF有

UTF-8 可变字节序列,用1到4个字节表示一个码点
UTF-16 可变字节序列,用2或4个字节表示一个码点
UTF-32 固定字节序列,用4个字节表示一个码点

UTF-8对ASCⅡ编码是兼容的,都是一个字节,超过U+07FF的部分则用了复杂的转换方式来映射Unicode,具体不再详述。

UTF-16对于BMP的码点,采用2个字节进行编码,而BMP之外的码点,用4个字节组成代理对(surrogate pair)来表示。其中前两个字节范围是U+D800到U+DBFF,后两个字节范围是U+DC00到U+DFFF,通过以下公式完成映射(H:高字节 L:低字节 c:码点)
H = Math.floor((c-0x10000) / 0x400)+0xD800
L = (c – 0x10000) % 0x400 + 0xDC00

比如

UCS(Universal Character Set)通用字符集,是一个ISO标准,目前与Unicode可以说是等价的。
相对于UTF,UCS也有自己的转换方法(编码)。如

UCS-2 用2个字节表示BMP的码点
UCS-4 用4个字节表示码点

UCS-2是一个过时的编码方式,因为它只能编码基本平面(BMP)的码点,在BMP的编码上,与UTF-16是一致的,所以可以认为是UTF-16的一个子集。
UCS-4则与UTF-32等价,都是用4个字节来编码Unicode。

javascript字符处理

辣莫,js到底是用的啥编码呢?答案是UCS-2。咦,刚刚不是说UCS-2过时了吗?首先看下年表

1990 UCS-2 诞生
1995.5 JavaScript 诞生
1996.7 UTF-16 诞生

也就是说,Brendan Eich在写JS的时候,UTF-16还没问世,所以只能用UCS-2的方式来处理字符,也因此留下了隐患。

坑1——length属性

先看一个简单的例子:

>”\uD83D\uDCA9″ === “
>true
>”
>2

因为”
怎么破?可以用万能的正则匹配

var regexAstralSymbols = /[\uD800-\uDBFF][\uDC00-\uDFFF]/g; // 匹配UTF-16的代理对

function countSymbols(string) {

return string

// 把代理对改为一个BMP的字符.

.replace(regexAstralSymbols, '_')

// …这时候取长度就妥妥的啦.

.length;

}

countSymbols('); // 1

   

坑2——反转字符串

js里怎么反转(reverse)字符串?相信有些同学已经想到了一个极简的方案

function reverse(str) {

    return str.split('').reverse().join('');

}

   

js虽没有直接的反转字符串的API,但是数组有啊,转数组反转之后再转回字符串,嘿嘿嘿,是不是很机智?这时候Unicode大爷又出来打脸了:你们呐,sometimes naive!

拿刚才的函数反转带有

reverse('这是一坨)

"��坨一是这"

   

�的Unicode码点是+UFFFD,通常用来表示Unicode转换时无法识别的字符(也就是乱码)


怎么破?

ES6的Array.from支持代理对的解析

function reverse(string) {

return Array.from(string).reverse().join('');

}

   

 使用 Esrever (reverse反转之后就是esrever…)

坑3——码点与字符互转

String.fromCharCode可以将一个码点转换为字符,比如


String.fromCharCode(0x0041)

'A'

   

但超过BMP平面的就跪了。


>> String.fromCharCode(0x1F4A9) // U+1F4A9

'' // U+F4A9, not U+1F4A9

   

事实上这个API是支持俩参数的,分别是代理对的高低字节。所以需要通过公式计算出对应的高低字节


>> String.fromCharCode(0xD83D, 0xDCA9)

' // U+1F4A9

>> '.charCodeAt(0)

0xD83D

   

一个字,蛋疼!
怎么破? ES6大法好。

>> String.fromCodePoint(0x1F4A9)

' // U+1F4A9

>> '.codePointAt(0)

0x1F4A9

   

坑4——正则匹配

正则匹配符.只能匹配单个“字符”,但js将代理对当成两个单独的“字符”处理,所以匹配不到任何辅助平面字符。

>> /foo.bar/.test('foo)

false

   

思考一下,什么正则表达式可以表示任何Unicode字符? 显然.是不够的,因为它不能匹配辅助平面字符或者换行符。那么用\s\S呢?

>> /^[\s\S]$/.test(')

false

   

怀疑人生了~~正确的匹配任意Unicode字符的正则如下:


>> /[\0-\uD7FF\uE000-\uFFFF]|[\uD800-\uDBFF][\uDC00-\uDFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF]/.test(') // wtf

true

   

怎么破? ES6给出一个简单的方法——增加一个u标志

>> /foo.bar/u.test('foo)

true

   

注意:这里的.还是不能匹配换行符。

ES6的Unicode支持

从上面的例子中可以看出,ES6已经在很努力地填坑了。对于Unicode字符,ES6支持新的表示方法 \u{1F4A9} 加上花括号后,可以把码点直接填进去来表示,而不用去计算代理对。再补充2点:
1. 为了向后兼容,字符串的length属性还是用双字节判断的,所以要用Array.from(str).length。
2. 遍历字符串的时候,可以用for(let s of str) {}

 

本文由职坐标整理发布,欢迎关注职坐标WEB前端JavaScript频道,获取更多JavaScript知识!

本文由 @沉沙 发布于职坐标。未经许可,禁止转载。
喜欢 | 0 不喜欢 | 0
看完这篇文章有何感觉?已经有0人表态,0%的人喜欢 快给朋友分享吧~
评论(0)
后参与评论

您输入的评论内容中包含违禁敏感词

我知道了

助您圆梦职场 匹配合适岗位
验证码手机号,获得海同独家IT培训资料
选择就业方向:
人工智能物联网
大数据开发/分析
人工智能Python
Java全栈开发
WEB前端+H5

请输入正确的手机号码

请输入正确的验证码

获取验证码

您今天的短信下发次数太多了,明天再试试吧!

提交

我们会在第一时间安排职业规划师联系您!

您也可以联系我们的职业规划师咨询:

小职老师的微信号:z_zhizuobiao
小职老师的微信号:z_zhizuobiao

版权所有 职坐标-一站式IT培训就业服务领导者 沪ICP备13042190号-4
上海海同信息科技有限公司 Copyright ©2015 www.zhizuobiao.com,All Rights Reserved.
 沪公网安备 31011502005948号    

©2015 www.zhizuobiao.com All Rights Reserved

208小时内训课程