Stay hungry, Stay foolish

0%

utf8中文正则匹配

问题

用正则表达式匹配微博中@用户昵称。

*如果您想直接看答案,选中这里->答案:/^[x{4e00}-x{9fa5}]/u

微博里的用户昵称规则是中文、英文、数字,减号和下划线。

前端JS的验证规则是new RegExp(“^[0-9a-zA-Zu4e00-u9fa5_-]*$”).test();

第一个方案

1
'/@([0-9a-zA-Zu4e00-u9fa5_-]*)/'

运行了一下,出错了:

1
Warning: preg_match(): Compilation failed: PCRE does not support L, l, N, P, p, U, u, or X at offset

在网上搜了一下:

PHP正则表达式中不支持下列 Perl 转义序列:L, l, N, P, p, U, u, or X
在 UTF-8 模式下,允许用“x{…}”,花括号中的内容是表示十六进制数字的字符串。原来的十六进制转义序列 xhh 如果其值大于 127 的话则匹配了一个双字节 UTF-8 字符。

第二个解决方案

1
"/^[x80-xff_a-zA-Z0-9]{3,15}$"

这个貌似只能匹配全角字符,错误提示没了,但是匹配的不全,还出乱码。

第三个解决方案

  • GB2312汉字字母数字下划线正则表达式
1
preg_match("/^[".chr(0xa1)."-".chr(0xff)."A-Za-z0-9_]+$/",$str))
  • UTF-8汉字字母数字下划线正则表达式
1
preg_match("/^[x{4e00}-x{9fa5}A-Za-z0-9_]+$/u",$str))

这个看似很靠谱但是还是报错了:

1
Compilation failed: character value in x{...} sequence is too large

最后用这个错误信息搜到了eqida的技术日志上的解决方案

第四个解决方案

1
preg_match("/^[x{4e00}-x{9fa5}]{1,4}/u",$str);

加入参数u,错误消失,匹配正确。

博客园上的一篇介绍

一般来说,指定代码点的形式有3种:『uxxxx』、『u{xxxx}』、『x{xxxx}』(其中的xxxx为编码的值,u之后必须有4位 16进制数字)。.NET、Java、JavaScript和Python使用第一种方式,而PHP和Ruby使用第二种方式(Ruby 1.9以上版本才支持这种表示法),PHP使用第三种方式。

而且PHP必须指定Unicode模式。

据说打赏我的人,代码没有BUG