在此之前,项目中使用正则匹配汉字的表达式都是 /[\u4e00-\u9fa5]/
,虽然常用,但是一直未深究其所以然。
首先,我们需要了解汉文和汉字这两个基础概念:
汉字文化圈中的许多国家或地区都对汉字提出了自己的编码标准,而 Unicode 将这些标准加总在一起进行统一编码,力求实现原标准与 Unicode 编码之间的无损转换。
Unicode,GBK 和 UTF-8 有什么区别?很多人总是将它们混淆在一起,傻傻分不清,实际上它们不是同领域的概念。
我们常见的 Unicode、 ASCII 是一种字符集(character set),其作用是用一系列数字来表示字符(character),这些数字有时也称为码点(code points)。
例如 ASCII 码是美国制定了一套字符编码,ASCII 码使用一个字节来表示一个字符,一共规定了 128 个字符的编码,对应英语字符与二进制位之间的关系。
但对于一些亚洲国家的文字,比如中文,这些符号远远不够,必须使用多个字节表示一个符号。比如简体中文的 GB2312 编码方式,使用两个字节表示一个字符,最多可以表示 256 x 256 = 65536 个符号。
由于世界上存在着多种编码方式,同一个二进制数字可以被解释成不同的符号。如果用错误的编码方式解读一段文本,就会出现乱码。由此 Unicode 应运而生。
Unicode 是一个统一编码集合,类似于世界语,它将世界上所有符号都赋予了一个第一无二的编码。由于每个字符的编码都是唯一的,这样避免了字符编码混乱的问题。
有了字符集,我们现在可以用任意数字来表示现实中的字符了。需要注意的是 Unicode 只是一个符号集,它只规定了符号的二进制代码,却没有规定这个二进制代码应该如何存储。所以字符要保存在计算机中,必须要先经过编码。
UTF-8 是 Unicode 的字符编码方式之一,同时也是互联网上使用最广的一种 Unicode 的实现方式。它规定了 Unicode 符号的存储方式。UTF-8 最大的一个特点,就是它是一种变长的编码方式。它可以使用 1~4 个字节表示一个符号,根据不同的符号而变化字节长度。
GBK(汉字内码扩展规范)和 UTF-8 类似,是一种中文字符编码方式。
总结:ASCII 和 Unicode 是一种字符集,而 UTF-8 和 GBK 是一种字符编码方式。两者不是一类事物, 是无法进行对比的。
介绍完字符集和字符编码之后,回到正题,我们已经知道「汉字」是汉文的基本单元,但这里的「汉字」具体指代哪些字符集呢?
Unicode 从语义(semantic)、抽象字形(abstract shape),具体字形(typeface)三个维度出发,把不同编码标准里「起源相同、本义相同、形状一样或稍异」的汉字赋予相同编码,这些被编码的字符称为中日韩统一表意文字,即我们上面所提到的「汉字」。如果把它们全部列举出来写成正则表达式,那么就是技术上完整的匹配汉字的正则表达式了。
我们一开始所提到的正则表达式 /[\u4e00-\u9fa5]/
匹配区域对应的是 Unicode 1.0.1 就收录进来的中日韩统一表意文字区块,在 Unicode 3.0 以前,这个正则表达式确实给出了所有汉字的编码。换言之,从 1992 年到 1999 年,这个正则表达式确实是正确的,想必这个表达式已经有 20 年了。
然而时光飞逝,Unicode 在 2017 年 6 月发布了 10.0.0 版本。在这 20 年间,Unicode 添加了许多汉字。这些新增的汉字并不在上面这个正则表达式匹配的区域中,所以我们的正则也需要与时俱进匹配最新的 Unicode 标准。
而这个问题的标准答案正则即是:
const HanZi = /\p{Unified_Ideograph}/u
\u
是 ECMAScript 2015 定义的正则表达式标志,意味着将表达式作为 Unicode 码点序列;\p
是 ECMAScript 2018 定义的正则表达式 Unicode 属性转义,它赋予了我们根据 Unicode 字符的属性数据构造表达式的能力;Unified_Ideograph
是 Unicode 字符的一个二值属性,对于汉字,其取值为 Yes,否则为 No。因此 \p{Unified_Ideograph}
匹配所有满足 Unified_Ideograph=yes
的 Unicode 字符,而它的底层实现由运行时所依赖的 Unicode 版本决定,开发者不需要知道汉字的具体 Unicode 码点范围。
Chrome 64 以上以及 Safari 11.1 以上都支持正则表达式 Unicode 属性转义。对于其他浏览器,推荐使用 7.7 版本的 @babel/env
转译配置将带有属性转义的正则表达式转为 Unicode 码点正则表达式。
{
"presets": ["@babel/env"]
}
参考文章: JavaScript 正则表达式匹配汉字