说PCRE可能不一定能反应过来,但是说PHP中的perl兼容正则表达式,肯定是早闻大名了。一直以来都觉得PHP都Unicode字符支持不错,从变量名到函数名,基本上标识符无一例外都支持Unicode。所以在PHP中类似:

这样的用法,简直让人”不亦乐乎“。想一想每次写程序,都要为变量名怎么取、怎么缩略而头疼的时候,想想每次为使用匈牙利命名法还是GNU命名法而头疼的时候,这一特性,不是让一切问题迎刃而解了么?

广告扯得有点远了,回来看看今天的主题——正则表达式中的中文字符。

正则表达式非常强大(虽然也有点让人觉得很复杂),但是其强大甚至是变态的匹配能力不得不让人折服。PHP中既有采用了比较彪悍的Perl兼容的正则函数,也有采用POSIX兼容的正则函数。相比之下前者无疑效率更高。这些函数就是由PHP中的PCRE库默认提供的。

在使用正则表达式的时候,有一个非常头疼的问题,就是中文(准确说是非ASCII字符)的匹配。通常会按照一个字节一个字节来匹配,这样经常是匹配不了的,即便是匹配出来了,结果也是乱码的(准确说只是匹配出Unicode字符的第一个字节,然后提取出来的也是第一个字节,自然是乱码了)。

作为对Unicode具有良好支持的PHP,自然也考虑到了这一点。在Perl兼容的情况下,增加了一种”u“模式,用于Unicode字符的正则匹配。举例来说:

结果是空的,匹配失败。因为这里的”不“字,虽然逻辑上是一个字,但实际上占用的是3个字节(UTF8编码,其他编码不同)。所以上面这样匹配不出来。改成这样就可以了:

也许你会说,既然这样,大不了以后我就直接把中文字当成三个字节的,不久仍然没问题了?这个应用看上去是没问题了,可是如果改成下面这个例子:

这个表达式是能够匹配出来的,但是匹配出来的是乱码。因为看上去是匹配今天、明天、后天三个中的一个,实际上匹配的是今、明、后这三个字的Unicode码中的三个字节。假设他们的Unicode码分别是0xaabbcc、0xddeeff 0x112233。那么这个表达式实际上匹配的是0xaa、0xbb、0xcc、0xdd、0xee、0xff、0x11、0x22、0x33中任意一个字符后接”天不用上课了吧“。所以匹配出来的自然是乱码。

遇到这种情况,也许你会选择使用下面的方式来代替:

实践证明,也是可以的。这里实际上不再是匹配一个字(今、明、后),而是一个由三个字节组成的表达式相或的结果。所以,这个方法虽然解决了这个问题,但是有点”转移战线“、”偷梁换柱“的嫌疑。我们再用一个例子来说明这个问题:

按道理,这应该是能够匹配的。但是匹配出来的结果却是乱码。这里就无法用上面的方式解决了。所以我们还是切入我们今天的正题,用PHP开发人员给我们提供的功能——unicode模式正则匹配,请看下面的例子:

只需要在正则匹配串后面加上一个u模式字符(Unicode模式),就一切搞定了。同样的,在上面几个例子中,完全可以不用修改表达式,只需要在后面加上一个u模式字符串,就能搞定了。另外,PHP的正则还有好几个模式可用,大家可以去PHP手册中围观。