> For the complete documentation index, see [llms.txt](https://chanshiyu.gitbook.io/blog/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://chanshiyu.gitbook.io/blog/qian-duan/javascript/10-zheng-ze-pi-pei-han-zi.md).

# 10 正则匹配汉字

在此之前，项目中使用正则匹配汉字的表达式都是 `/[\u4e00-\u9fa5]/`，虽然常用，但是一直未深究其所以然。

## 汉文与汉字

首先，我们需要了解**汉文**和**汉字**这两个基础概念：

* \*\*汉文（Han Script）\*\*是汉语、日本语、朝鲜语、韩国语的书写系统中的一种文字；
* \*\*汉字（CJK Ideograph）\*\*是汉文的基本单元。

汉字文化圈中的许多国家或地区都对汉字提出了自己的编码标准，而 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 标准。

而这个问题的标准答案正则即是：

```javascript
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 正则表达式匹配汉字](https://zhuanlan.zhihu.com/p/33335629)
