一个可能有 20 年历史的正则表达式
在谷歌搜索「JavaScript 正则表达式匹配汉字」的时候,前几条结果全都是/[\u4e00-\u9fa5]/。没有人怀疑这个正则表达式有什么问题,那么在 2018 年的今天,让我们站在 Chrome 64 的肩膀上,放飞一下自我。

汉文(Han Script)是汉语、日本语、朝鲜语、韩国语的书写系统中的一种文字(Script),越南语在早期也曾在书写系统中使用汉文[1]。汉字(CJK Ideograph)是汉文的基本单元。各国都对汉字提出了自己的编码标准,Unicode 将这些标准加总在一起进行统一编码,力求实现原标准与 Unicode 编码之间的无损转换。Unicode 从语义(semantic)、抽象字形(abstract shape),具体字形(typeface)三个维度[2]出发,把不同编码标准里「起源相同、本义相同、形状一样或稍异」的汉字赋予相同编码,这些被编码的字符称为中日韩统一表意文字(下文我们提到的「汉字」,如果不加说明,均指代中日韩统一表意文字)。如果把它们全部列举出来写成正则表达式,那么就是技术上完整的匹配汉字的正则表达式了。

正则表达式/[\u4e00-\u9fa5]/的意思是匹配所有从 U+4E00, cjk unified ideograph-4e00 到 U+9FA5, cjk unified ideograph-9fa5 的字符。这一段区域对应的是 Unicode 1.0.1 就收录进来的中日韩统一表意文字(CJK Unified Ideographs)区块,在 Unicode 3.0 加入扩展 A 区以前,这个正则表达式确实给出了所有汉字的编码。换言之,从1992年到1999年,这个正则表达式确实是正确的,想必这个表达式已经有20年历史了。

匹配所有统一表意文字
然而时光飞逝,Unicode 在2017年6月发布了10.0.0版本。在这20年间,Unicode 添加了许多汉字。比如 Unicode 8.0 添加的 109 号化学元素「鿏(⿰⻐麦)」,其码点是 9FCF,不在这个正则表达式范围中。而如果我们期望程序里的/[\u4e00-\u9fa5]/可以与时俱进匹配最新的 Unicode 标准,显然是不现实的事情。因此,我们需要换一个思路,写一个无需维护的正则表达式:

/\p{Unified_Ideograph}/u
其中\u是 ECMAScript 2015 定义的正则表达式标志,意味着将表达式作为 Unicode 码点序列。\p是正在提案阶段的正则表达式 Unicode 属性转义,它赋予了我们根据 Unicode 字符的属性数据[3]构造表达式的能力。Unified_Ideograph是 Unicode
字符的一个二值属性,对于汉字,其取值为 Yes,否则为 No。因此\p{Unified_Ideograph}匹配所有满足Unified_Ideograph=yes的 Unicode 字符,而它的底层实现由运行时所依赖的 Unicode 版本决定,开发者不需要知道汉字的具体 Unicode 码点范围。

容易混淆的其他 Unicode 属性转义表达式
/\p{Ideographic}/u

这个表达式匹配所有满足Ideographic=yes的 Unicode 字符。我们先看一下 UAX #44 对这个属性的解释[4] :

Characters considered to be CJKV (Chinese, Japanese, Korean, and Vietnamese) or other siniform (Chinese writing-related) ideographs. This property roughly defines the class of “Chinese characters” and does not include characters of other logographic scripts such as Cuneiform or Egyptian Hieroglyphs.
这个属性表明该字符属于 CJKV 表意文字或者与汉语书写相关的其他表意文字(如西夏文、女书),这个属性粗略地定义了「中文字符」的分类。我们查看Unicode 10.0.0 字符属性列表可以知道,在 Unicode 10.0.0 中,Ideographic 属性为 yes 的字符有

3006 ; Ideographic # Lo IDEOGRAPHIC CLOSING MARK
3007 ; Ideographic # Nl IDEOGRAPHIC NUMBER ZERO
3021…3029 ; Ideographic # Nl [9] HANGZHOU NUMERAL ONE…HANGZHOU NUMERAL NINE
3038…303A ; Ideographic # Nl [3] HANGZHOU NUMERAL TEN…HANGZHOU NUMERAL THIRTY
3400…4DB5 ; Ideographic # Lo [6582] CJK UNIFIED IDEOGRAPH-3400…CJK UNIFIED IDEOGRAPH-4DB5
4E00…9FEA ; Ideographic # Lo [20971] CJK UNIFIED IDEOGRAPH-4E00…CJK UNIFIED IDEOGRAPH-9FEA
F900…FA6D ; Ideographic # Lo [366] CJK COMPATIBILITY IDEOGRAPH-F900…CJK COMPATIBILITY IDEOGRAPH-FA6D
FA70…FAD9 ; Ideographic # Lo [106] CJK COMPATIBILITY IDEOGRAPH-FA70…CJK COMPATIBILITY IDEOGRAPH-FAD9
17000…187EC ; Ideographic # Lo [6125] TANGUT IDEOGRAPH-17000…TANGUT IDEOGRAPH-187EC
18800…18AF2 ; Ideographic # Lo [755] TANGUT COMPONENT-001…TANGUT COMPONENT-755
1B170…1B2FB ; Ideographic # Lo [396] NUSHU CHARACTER-1B170…NUSHU CHARACTER-1B2FB
20000…2A6D6 ; Ideographic # Lo [42711] CJK UNIFIED IDEOGRAPH-20000…CJK UNIFIED IDEOGRAPH-2A6D6
2A700…2B734 ; Ideographic # Lo [4149] CJK UNIFIED IDEOGRAPH-2A700…CJK UNIFIED IDEOGRAPH-2B734
2B740…2B81D ; Ideographic # Lo [222] CJK UNIFIED IDEOGRAPH-2B740…CJK UNIFIED IDEOGRAPH-2B81D
2B820…2CEA1 ; Ideographic # Lo [5762] CJK UNIFIED IDEOGRAPH-2B820…CJK UNIFIED IDEOGRAPH-2CEA1
2CEB0…2EBE0 ; Ideographic # Lo [7473] CJK UNIFIED IDEOGRAPH-2CEB0…CJK UNIFIED IDEOGRAPH-2EBE0
2F800…2FA1D ; Ideographic # Lo [542] CJK COMPATIBILITY IDEOGRAPH-2F800…CJK COMPATIBILITY IDEOGRAPH-2FA1D
Total code points: 96174
它们囊括了所有统一表意文字、西夏文及其组件、女书、中日韩兼容性字符、苏州码子、「〇」以及日本语中的书信结尾标志「〆」。使用/\p{Ideographic}/u来匹配汉字会过于宽泛。一是包含了西夏文、女书,二是只用于编码转换用的兼容字符也纳入其中。

/\p{Script=Han}/u

Script 属性[5]用来筛选满足下面条件的一组字符:

  1. 字符的书写形式具有共同的图像特征与文字流变
  2. 该组字符全部用来表达某个书写系统内的文本信息(textual information)

我们查看Unicode 10.0.0 Scripts可以知道,满足Script=Han的字符有

2E80…2E99 ; Han # So [26] CJK RADICAL REPEAT…CJK RADICAL RAP
2E9B…2EF3 ; Han # So [89] CJK RADICAL CHOKE…CJK RADICAL C-SIMPLIFIED TURTLE
2F00…2FD5 ; Han # So [214] KANGXI RADICAL ONE…KANGXI RADICAL FLUTE
3005 ; Han # Lm IDEOGRAPHIC ITERATION MARK
3007 ; Han # Nl IDEOGRAPHIC NUMBER ZERO
3021…3029 ; Han # Nl [9] HANGZHOU NUMERAL ONE…HANGZHOU NUMERAL NINE
3038…303A ; Han # Nl [3] HANGZHOU NUMERAL TEN…HANGZHOU NUMERAL THIRTY
303B ; Han # Lm VERTICAL IDEOGRAPHIC ITERATION MARK
3400…4DB5 ; Han # Lo [6582] CJK UNIFIED IDEOGRAPH-3400…CJK UNIFIED IDEOGRAPH-4DB5
4E00…9FEA ; Han # Lo [20971] CJK UNIFIED IDEOGRAPH-4E00…CJK UNIFIED IDEOGRAPH-9FEA
F900…FA6D ; Han # Lo [366] CJK COMPATIBILITY IDEOGRAPH-F900…CJK COMPATIBILITY IDEOGRAPH-FA6D
FA70…FAD9 ; Han # Lo [106] CJK COMPATIBILITY IDEOGRAPH-FA70…CJK COMPATIBILITY IDEOGRAPH-FAD9
20000…2A6D6 ; Han # Lo [42711] CJK UNIFIED IDEOGRAPH-20000…CJK UNIFIED IDEOGRAPH-2A6D6
2A700…2B734 ; Han # Lo [4149] CJK UNIFIED IDEOGRAPH-2A700…CJK UNIFIED IDEOGRAPH-2B734
2B740…2B81D ; Han # Lo [222] CJK UNIFIED IDEOGRAPH-2B740…CJK UNIFIED IDEOGRAPH-2B81D
2B820…2CEA1 ; Han # Lo [5762] CJK UNIFIED IDEOGRAPH-2B820…CJK UNIFIED IDEOGRAPH-2CEA1
2CEB0…2EBE0 ; Han # Lo [7473] CJK UNIFIED IDEOGRAPH-2CEB0…CJK UNIFIED IDEOGRAPH-2EBE0
2F800…2FA1D ; Han # Lo [542] CJK COMPATIBILITY IDEOGRAPH-2F800…CJK COMPATIBILITY IDEOGRAPH-2FA1D

# Total code points: 89228
它们囊括了所有统一表意文字、中日韩兼容性字符、苏州码子、「〇」、「〆」、「々」以及字典常用的部首。从前面汉文(Han Script)与汉字(CJK Ideograph)的关系我们可以知道,/\p{Script=Han}/u匹配的是汉文作为一个字符集里面的所有字符,因此它包括了部首、「々」等字符,这些字符要么当它们独立存在的时候没有语言意义(部首独立存在是一个符号),要么无法独立存在(「々」依赖于所修饰的汉字)。所以汉字是汉文的一个单元,汉文除了包含汉字以外,还包括这些符号、数字、修饰符。因此使用/\p{Script=Han}/u来匹配汉字是混淆了汉文与汉字的概念范围。

浏览器兼容性支持
JavaScript

截至2018年1月,只有 Chrome 64 支持正则表达式 Unicode 属性转义。对于其他浏览器,我们需要用babel转译插件@babel/plugin-proposal-unicode-property-regex的底层将带有属性转义的正则表达式转为 Unicode 码点正则表达式或者 ES 5 的正则表达式。转译结果的在线演示可以在这里查看,用户可以自己在上面转译其他的 Unicode 属性转义正则表达式。我们在这里列举/\p{Unified_Ideograph}/u转译成Unicode 码点正则表达式的结果:

const regex = /\p{Unified_Ideograph}/u;
// transpiled to ES6:
const regex = /[\u3400-\u4DB5\u4E00-\u9FEA\uFA0E\uFA0F\uFA11\uFA13\uFA14\uFA1F\uFA21\uFA23\uFA24\uFA27-\uFA29\u{20000}-\u{2A6D6}\u{2A700}-\u{2B734}\u{2B740}-\u{2B81D}\u{2B820}-\u{2CEA1}\u{2CEB0}-\u{2EBE0}]/u;
从上面这个正则表达式可以知道,转译的结果严格跟 Unicode 10.0.0 中 Unified_Ideograph 属性为 yes 的字符

3400…4DB5 ; Unified_Ideograph # Lo [6582] CJK UNIFIED IDEOGRAPH-3400…CJK UNIFIED IDEOGRAPH-4DB5
4E00…9FEA ; Unified_Ideograph # Lo [20971] CJK UNIFIED IDEOGRAPH-4E00…CJK UNIFIED IDEOGRAPH-9FEA
FA0E…FA0F ; Unified_Ideograph # Lo [2] CJK COMPATIBILITY IDEOGRAPH-FA0E…CJK COMPATIBILITY IDEOGRAPH-FA0F
FA11 ; Unified_Ideograph # Lo CJK COMPATIBILITY IDEOGRAPH-FA11
FA13…FA14 ; Unified_Ideograph # Lo [2] CJK COMPATIBILITY IDEOGRAPH-FA13…CJK COMPATIBILITY IDEOGRAPH-FA14
FA1F ; Unified_Ideograph # Lo CJK COMPATIBILITY IDEOGRAPH-FA1F
FA21 ; Unified_Ideograph # Lo CJK COMPATIBILITY IDEOGRAPH-FA21
FA23…FA24 ; Unified_Ideograph # Lo [2] CJK COMPATIBILITY IDEOGRAPH-FA23…CJK COMPATIBILITY IDEOGRAPH-FA24
FA27…FA29 ; Unified_Ideograph # Lo [3] CJK COMPATIBILITY IDEOGRAPH-FA27…CJK COMPATIBILITY IDEOGRAPH-FA29
20000…2A6D6 ; Unified_Ideograph # Lo [42711] CJK UNIFIED IDEOGRAPH-20000…CJK UNIFIED IDEOGRAPH-2A6D6
2A700…2B734 ; Unified_Ideograph # Lo [4149] CJK UNIFIED IDEOGRAPH-2A700…CJK UNIFIED IDEOGRAPH-2B734
2B740…2B81D ; Unified_Ideograph # Lo [222] CJK UNIFIED IDEOGRAPH-2B740…CJK UNIFIED IDEOGRAPH-2B81D
2B820…2CEA1 ; Unified_Ideograph # Lo [5762] CJK UNIFIED IDEOGRAPH-2B820…CJK UNIFIED IDEOGRAPH-2CEA1
2CEB0…2EBE0 ; Unified_Ideograph # Lo [7473] CJK UNIFIED IDEOGRAPH-2CEB0…CJK UNIFIED IDEOGRAPH-2EBE0
# Total code points: 87882
严格对应。因此转译是正确的。

该插件还可以使用

{
“plugins”: [
["@babel/plugin-proposal-unicode-property-regex", { “useUnicodeFlag”: false }]
]
}
配置将表达式转成 ES5 的传统的以字符的 UTF16 表示为序列的字符串,这里不再赘述。

input 元素的 pattern 属性

在前端技术中,除了JavaScript会用到正则表达式,HTML 里<input>元素的pattern属性也会用到正则表达式。与 JavaScript 相比,pattern不支持设置正则表达式的标志位,因此 HTML 标准中强制规定了 input 元素的 pattern 属性需要施加unicode标志 [6]。目前只有 Chrome 53+, Firefox 遵循了这一标准,其他的浏览器暂未支持。

在 React/Angular/Vue.js 三大前端框架中,Angular 提供了近似于 pattern 的指令 ngPattern。目前ngPattern尚未施加unicode标志 [7]。AngularJS 的 ngPattern directive 仍未施加。

在大部分情况,是否施加unicode标志不会对正则表达式产生语义区别。主要的差别在于,在使用\u{10000}表示 Unicode 码点字符情形,正则表达式/\u{10000}/代表匹配u一万次,/\u{10000}/u匹配字符\u{10000}一次;/./只匹配 BMP 平面的字符,/./u匹配所有平面的字符。

由于 Unicode 属性转义正则表达式依赖于标识位\u,因此下面的用法目前只能在 Chrome 下使用:

因此,如果需要兼容其他浏览器,可以使用转译插件的底层库regexpu-core在 js 层转换正则表达式,再把转换结果输送到 HTML 模版中。

const rewritePattern = require(“regexpu-core”);
rewritePattern(’\p{Unified_Ideograph}’, ‘u’, {
‘unicodePropertyEscape’: true,
‘useUnicodeFlag’: false
});
// → ‘/(?:[\u3400-\u4DB5\u4E00-\u9FEA\uFA0E\uFA0F\uFA11\uFA13\uFA14\uFA1F\uFA21\uFA23\uFA24\uFA27-\uFA29]|[\uD840-\uD868\uD86A-\uD86C\uD86F-\uD872\uD874-\uD879][\uDC00-\uDFFF]|\uD869[\uDC00-\uDED6\uDF00-\uDFFF]|\uD86D[\uDC00-\uDF34\uDF40-\uDFFF]|\uD86E[\uDC00-\uDC1D\uDC20-\uDFFF]|\uD873[\uDC00-\uDEA1\uDEB0-\uDFFF]|\uD87A[\uDC00-\uDFE0])/’

总结
/[\u4e00-\u9fa5]/是错的,不要用二十年前的正则表达式了
/\p{Unified_Ideograph}/u是正确的,不需要维护,匹配所有汉字。这里\p是 Unicode 属性转义正则表达式。
/\p{Ideographic}/u/\p{Script=Han}/u 匹配了除了汉字以外的其他一些字符,在「汉字匹配正则表达式」这个需求下,是错的。
目前只有 Chrome 支持 Unicode 属性转义正则表达式。对其他环境,使用@babel/plugin-proposal-unicode-property-regexregexpu-core 进行优雅降级。
参考资料
[1] Unicode 10.0.0 第六章第一节,书写系统 http://www.unicode.org/versions/Unicode10.0.0/ch06.pdf

[2] Unicode 10.0.0 第十八章第一节,东亚 http://www.unicode.org/versions/Unicode10.0.0/ch18.pdf

[3] Unicode 10.0.0 字符属性列表 http://www.unicode.org/Public/10.0.0/ucd/PropList.txt

[4] UAX #44 第 20 版的属性说明 http://www.unicode.org/reports/tr44/tr44-20.html#Property_Definitions

[5] UAX #24 第 27 版 http://www.unicode.org/reports/tr24/tr24-27.html#Introduction

[6] HTML 标准中input元素的pattern属性 https://html.spec.whatwg.org/multipage/input.html#the-pattern-attribute

[7] 给ngPattern施加unicode标志 https://github.com/angular/angular/pull/20819

JavaScript 正则表达式匹配汉字相关推荐

  1. python正则匹配固定汉字_Python用正则表达式匹配汉字

    原博文 2019-11-07 19:54 − #### Python用正则表达式匹配汉字 ##### 匹配多个汉字,不包括空格 ```python import re res = re.match(r ...

  2. python - 正则表达式匹配汉字

    python - 正则表达式匹配汉字 Unicode 编码中汉字编码的范围在 \u4e00 到 \u9fa5之间 所以 匹配任意汉字: [\u4e00-\u9fa5] 匹配某个汉字可以把汉字转换成Un ...

  3. 使用正则表达式匹配汉字字符串

    使用正则表达式匹配汉字字符串: 1.一个汉字:[\u4E00-\u9FA5] 2.多个汉字,包括空格:[\u4E00-\u9FA5\\s]+ 3.多个汉字,不包括空格:[\u4E00-\u9FA5]+ ...

  4. js正则表达式匹配汉字

    假期老师布置了一个大作业,需要用正则表达式匹配一下 uniocde编码的汉字,但是网上千篇一律都是 /[\u4e00-\u9fa5]/ ,结果当然没有通过老师的作业检查(当然问题不仅仅只有这一点). ...

  5. java之正则表达式匹配汉字

    转自:http://blog.csdn.net/csdn_yaobo/article/details/48377757 本来是要收藏的,但是不知怎么了,点了收藏没有反应,只好先转载一下,等能收藏的时候 ...

  6. [Javascript] 正则表达式匹配的常用方法总结

    目录标题 正则 正则表达式对象的lastIndex属性的作用是什么 举一个使用了RegExp.test()方法的例子 再举一个使用例子RegExp.exec()的例子 什么是捕获组 sticky 标志 ...

  7. JAVA正则表达式怎么表达汉字_Java的正则表达式匹配汉字

    [Java 提供了功能强大的正则表达式API,在java.util.regex 包下.本教程介绍如何使用正则表达式API.] 刚才有同事问,顺便发出来记一下. 以前读<精通正则表达式>的时 ...

  8. python正则表达式匹配汉字

    [\u4e00-\u9fa5] 以上正则表达式只匹配一个汉字 如果要匹配指定个数可用 [\u4e00-\u9fa5]{n} n是具体的数字 例子: import re b="中国河南省郑州市 ...

  9. 正则表达式匹配汉字/英文(数字)

    匹配条件 只能包含汉字.英文.数字 汉字英文必须存在其中一个 数字任意 正则表达式 /^[0-9]*([A-Za-z]|[\u4E00-\u9FA5])+[0-9A-Za-z\u4E00-\u9FA5 ...

最新文章

  1. python调用exe程序 传入参数_关于使用c#调用python脚本文件,脚本文件需要传递参数...
  2. gitlab介绍、安装及gitlab CI、与jenkins的对比
  3. BeetleX服务网关流量控制
  4. 访问控制 > 教程 > 利用标签对ECS实例进行分组授权
  5. 我们自研的那些Devops工具
  6. memcache、redis
  7. 安装虚拟机报错 This kernel requires an X86-64 CPU,but only detected an i686 CPU
  8. 互联网晚报 | 12月7日 星期二 | 阿里新设两大数字商业板块;B站宣布迈入8K超高清时代;中国物流集团正式成立...
  9. 设计一个巴特沃斯低通滤波器
  10. 什么是存储过程,存储过程的作用及优点
  11. 解锁计算机桌面,电脑锁屏按什么键解锁
  12. PS入门(1-7) HSB色彩模式
  13. 关于宋宝华linux驱动学习视频的读后感
  14. 【校园卡】更新联通校园卡套餐海报及常规操作,校园卡最新消息及选购建议,增加评论功能...
  15. vue中控制台报错[WDS] Disconnected的解决办法
  16. VS2019菜单栏的项目、生成、格式三个菜单栏不见了
  17. C Primer Plus 读书笔记(二)
  18. 关于计算机的英语作文带翻译,关于网络的英语作文带翻译
  19. 还在if-else行走天下?试试【策略模式】吧
  20. 已知有十六支男子足球队,参加2008 北京奥运会。写一个程序,把这16 支球队随机分为4 个组。

热门文章

  1. 【微信小程序】-- 使用 npm 包 - API Promise化(四十二)
  2. google源码下载方法
  3. 【SQL怎么写】查询同一张表中同一个ID对应最新时间的数据记录
  4. 【学习笔记】URL和DOI是什么?有什么区别?
  5. Cesium双屏对比
  6. (二)编译PVE内核5.10.6-1-pve及安装内核补丁fullconeNat
  7. Python语言快速入门(上)
  8. 第三章:电子商务平台选择
  9. HTML+css 基础语法
  10. 数据压缩读书笔记——线性代数的几何意义(五)