UTF-8模式正则表达式中的非ASCII字符

3

问题

尽管PHP手册声明:

"在UTF-8模式下,值大于128的字符不匹配任何POSIX字符类。"

为什么波斯数字在“UTF-8模式”下会匹配\d[[:digit:]]

阐述

在一个非相关问题的回答中提到,正则表达式中的\d不仅匹配ASCII数字09,还匹配波斯数字(۰ ۱ ۲ ۳ ۴ ۵ ۶ ۷)。

上述问题标记为,但这种行为也可以在PHP中观察到。基于此,我编写了以下“测试”:

$string = 'I have ۳ apples and 5 oranges';
preg_match_all('/\d+/', $string, $capture);

生成的数组$capture仅包含对5的匹配。

使用u修饰符开启“UTF-8模式”并运行以下代码:

$string = 'I have ۳ apples and 5 oranges';
preg_match_all('/\d+/u', $string, $capture);

结果是$capture包含对۳5的匹配。

注释

  • 本问题涉及PHP 5.6.22(截至目前最新版本)。
  • 两个测试都在明确使用C语言环境时执行。

如果您的字符串不是ASCII编码,那么没有使用u标志的第一个测试就毫无意义,因为匹配将按字节语义进行。如果您在SHIFT-JIS编码的字符串中使用\w,则可能会匹配某个字符的第二个字节。请参见此答案中的示例部分,了解有关非UTF模式和后果的说明:https://dev59.com/TWEi5IYBdhLWcg3wx-xs#30556342 - nhahtdh
1个回答

3
因为文档有误。不幸的是,这不是唯一一个出现这种情况的地方。
PHP在底层使用PCRE实现其preg_*函数。因此,PCRE的文档在这里是权威的。PHP的文档基于PCRE的文档,但看起来你发现了另一个错误。
以下是PCRE文档中的内容(重点是我的):

By default, characters with values greater than 128 do not match any of the POSIX character classes. However, if the PCRE_UCP option is passed to pcre_compile(), some of the classes are changed so that Unicode character properties are used. This is achieved by replacing certain POSIX classes by other sequences, as follows:

[:alnum:]  becomes  \p{Xan}
[:alpha:]  becomes  \p{L}
[:blank:]  becomes  \h
[:digit:]  becomes  \p{Nd}
[:lower:]  becomes  \p{Ll}
[:space:]  becomes  \p{Xps}
[:upper:]  becomes  \p{Lu}
[:word:]   becomes  \p{Xwd}
如果你在PHP的文档中深入挖掘,你会发现以下内容:

u (PCRE_UTF8)

这个修改器打开了PCRE的其他功能,与Perl不兼容。模式和主题字符串被视为UTF-8。从PHP 4.1.0或更高版本的Unix和从PHP 4.2.3开始在win32上提供此修改器。自PHP 4.3.5以来,模式和主题的UTF-8有效性已得到检查。无效的主题将导致preg_*函数匹配空;无效的模式将触发E_WARNING级别的错误。自PHP 5.3.4(resp.PCRE 7.3 2007-08-28)以来,五个和六个八位字节的UTF-8序列被认为是无效的;以前被认为是有效的UTF-8。

很遗憾,这是一个谎言。在PHP中,u修饰符表示PCRE_UTF8 | PCRE_UCP(UCP代表Unicode字符属性)。正如您可以从上面的文档中看到的那样,PCRE_UCP标志改变了\d\w等的含义。您的测试证实了这一点。
作为一个旁注,不要从一个正则表达式引擎推断出另一个正则表达式引擎的属性。这并不总是有效的(嘿,甚至this chart都忘记了PCRE_UCP选项)。

谢谢你详细的回答,Lucas。我使用这些信息提交了一个文档错误报告。让我们看看它是否会被解决或者修正。 - Linus Kleen

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接