理解 Javascript,PHP,SQL 匹配中文姓名正则表达式的区别

如何定义合法姓名

中文姓名非常多样化,想要真正区别出姓名和非姓名非常困难,但在这篇文章中,作为前提,我们认为合法的中文姓名由 1~10 个中文字符组成。

Javascript

知道了这个前提,容易得出 JS 的正则表达式——

姓名正则
1
/^([\u4e00-\u9fa5]{1,10})$/

通俗解释:

^ 匹配开头;
\u4e00 代表第一个汉字“一”;
\u9fa5 代表最后一个汉字“龥”;
[\u4e00-\u9fa5] 匹配一个中文字符;
{1,10} 这个中文字符出现 1 到 10 次;
$ 匹配结尾。

测试一下——

Javascript
1
2
3
4
5
const CHINESE_NAME_PATTERN = /^([\u4e00-\u9fa5]{1,10})$/;
console.log(CHINESE_NAME_PATTERN.test('张三')); // true
console.log(CHINESE_NAME_PATTERN.test('iMaeGoo')); // false
console.log(CHINESE_NAME_PATTERN.test('张三1')); // false
console.log(CHINESE_NAME_PATTERN.test('1张三')); // false

PHP

PHP 中不认 \u4e00,需要稍微变换一下语法——

PHP
1
2
3
4
5
<?php
echo preg_match('/^[\x{4e00}-\x{9fa5}]{1,10}$/u', '张三'); // 1
echo preg_match('/^[\x{4e00}-\x{9fa5}]{1,10}$/u', 'iMaeGoo'); // 0
echo preg_match('/^[\x{4e00}-\x{9fa5}]{1,10}$/u', '张三1'); // 0
echo preg_match('/^[\x{4e00}-\x{9fa5}]{1,10}$/u', '1张三'); // 0

结尾的 u 是什么意思?官方文档 中有描述:

u (PCRE_UTF8)
此修正符打开一个与 perl 不兼容的附加功能。 模式和目标字符串都被认为是 utf-8 的。 无效的目标字符串会导致 preg_* 函数什么都匹配不到; 无效的模式字符串会导致 E_WARNING 级别的错误。 PHP 5.3.4 后,5字节和6字节的 UTF-8 字符序列被考虑为无效(resp. PCRE 7.3 2007-08-28)。 以前就被认为是无效的 UTF-8。

MySQL

SQL 中也无法直接写 Unicode 转义 \u4e00,需要使用 HEX 将汉字转哈希之后,再匹配汉字的哈希值。

汉字的 HEX 范围:E4B880 ~ E9BEA5,所以可以用 e[4-9][0-9a-f]{4} 代表一个中文字符。

SQL
1
2
3
4
5
6
SELECT HEX('一'); # 'E4B880'
SELECT HEX('龥'); # 'E9BEA5'
SELECT HEX('张三') REGEXP '^(e[4-9][0-9a-f]{4}){1,10}$'; # 1
SELECT HEX('iMaeGoo') REGEXP '^(e[4-9][0-9a-f]{4}){1,10}$'; # 0
SELECT HEX('张三1') REGEXP '^(e[4-9][0-9a-f]{4}){1,10}$'; # 0
SELECT HEX('1张三') REGEXP '^(e[4-9][0-9a-f]{4}){1,10}$'; # 0

拓展

细心的读者可能会发现,HEX 输出的是大写字母 E,为什么匹配的时候用的是小写字母 e?其实这也是 MySQL 正则与 JS 正则的不同之一。

SQL 中,REGEXP 是不区分大小写匹配,REGEXP BINARY 是区分大小写匹配。
JS 中,/.../ 是区分大小写匹配,/.../i 是不区分大小写匹配。

评论