问题出现
在项目中遇到问题
“一二三四五?六七八九十”,被识别为10个字。
1、2、...、十 。呃...彷佛有点问题。应该是11个字的啊。
问题就出在?,这个字没有被识别。javascript
问题分析
看看代码:html
JavaScript容许直接用码点表示Unicode字符,写法是"反斜杠+u+码点"。码点有十六机制数表示。
可是,这种表示法对4字节的码点无效。ES6修正了这个问题,只要将码点放在大括号内,就能正确识别。
根据汉字unicode范围表发现经常使用的大多数汉字均可用u4E00-u9FA5来表示。而?的unicode码是U+20BB7,没有被包含。因此,咱们须要匹配汉字unicode范围表全部的汉字。java
可是, 编码相似u20BB7的4字节的码点不能直接被识别。咱们须要理解一下js使用的编码。
JavaScript使用哪种编码?
JavaScript用的是UCS-2!git
UCS的开发进度快于Unicode,1990年就公布了第一套编码方法UCS-2,使用2个字节表示已经有码点的字符。(那个时候只有一个平面,就是基本平面,因此2个字节就够用了。)UTF-16编码迟至1996年7月才公布,明确宣布是UCS-2的超集,即基本平面字符沿用UCS-2编码,辅助平面字符定义了4个字节的表示方法。
因为JavaScript只能处理UCS-2编码,形成全部字符在这门语言中都是2个字节,若是是4个字节的字符,会看成两个双字节的字符处理。JavaScript的字符函数都受到这一点的影响,没法返回正确结果。
unicodegithub
这么多符号,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-16正则表达式
究竟是把这两个字节看成一个字符仍是与后面的两个字节一块儿看成一个字符呢? 这里有一个很巧妙的地方,在基本平面内,从 U+D800 到
U+DFFF 是一个空段,即这些码点不对应任何字符。所以,这个空段能够用来映射辅助平面的字符。 辅助平面的字符位共有 220220
个,所以表示这些字符至少须要 20 个二进制位。UTF-16将这 20 个二进制位分红两半,前 10 位映射在 U+D800 到
U+DBFF,称为高位(H),后 10 位映射在 U+DC00 到
U+DFFF,称为低位(L)。这意味着,一个辅助平面的字符,被拆成两个基本平面的字符表示。 所以,当咱们遇到两个字节,发现它的码点在
U+D800 到 U+DBFF 之间,就能够判定,紧跟在后面的两个字节的码点,应该在 U+DC00 到 U+DFFF
之间,这四个字节必须放在一块儿解读。
总的来讲,一个辅助平面的字符,被拆成两个基本平面的字符表示。或者在ES6中能够用'u{20BB7}'来表示。函数
ES6的支持编码
for (let s of string ) { // ...
}prototype
为了获得字符串的正确长度,能够用下面的方式。code
Array.from(string).length [...string].length
问题解决
因此,咱们在正则中表示全部的汉字,须要将不能被直接识别的4字节识别,能够经过ES6的方式,也能够转换成基本平面来表示。咱们须要将全部的汉字区间都包含在匹配公式中,从上面的汉字unicode范围表看,本身来手写是很复杂。
还好,有Regenerate能够来完成这件事情。经过它咱们能够快速的表示出复杂的正则表达式。
export const character2unicode = regenerate() .addRange(0x4e00, 0x9fa5) .addRange(0x9fa6, 0x9fcb) .addRange(0x3400, 0x4db5) .addRange(0x20000, 0x2a6d6) .addRange(0x2a700, 0x2b734) .addRange(0x2b740, 0x2b81d) .addRange(0x2f00, 0x2fd5) .addRange(0x2e80, 0x2ef3) .addRange(0xf900, 0xfad9) .addRange(0x2f800, 0x2fa1d) .addRange(0xe815, 0xe86f) .addRange(0xe400, 0xe5e8) .addRange(0xe600, 0xe6cf) .addRange(0x31c0, 0x31e3) .addRange(0x2ff0, 0x2ffb) .addRange(0x3105, 0x3120) .addRange(0x31a0, 0x31ba) .toRegExp(); // character2unicode // /[\u2E80-\u2EF3\u2F00-\u2FD5\u2FF0-\u2FFB\u3105-\u3120\u31A0-\u31BA\u31C0-\u31E3\u3400-\u4DB5\u4E00-\u9FCB\uE400-\uE5E8\uE600-\uE6CF\uE815-\uE86F\uF900-\uFAD9]|[\uD840-\uD868\uD86A-\uD86C][\uDC00-\uDFFF]|\uD869[\uDC00-\uDED6\uDF00-\uDFFF]|\uD86D[\uDC00-\uDF34\uDF40-\uDFFF]|\uD86E[\uDC00-\uDC1D]|\uD87E[\uDC00-\uDE1D]/
以上就能够表示出汉字unicode范围编码的正则。感受能够很愉快的开工。
参考
https://mathiasbynens.be/note...
http://www.ruanyifeng.com/blo...
https://github.com/mathiasbyn...