17

我已经知道\w在 PCRE(特别是 PHP 的实现)中有时可以匹配一些非 ASCII 字符,具体取决于系统的语言环境,但是呢[a-z]

我不这么认为,但我在 Drupal 的一个核心文件(includes/theme.inc,简化版)中注意到了这些行:

// To avoid illegal characters in the class,
// we're removing everything disallowed. We are not using 'a-z' as that might leave
// in certain international characters (e.g. German umlauts).
$body_classes[] = preg_replace('![^abcdefghijklmnopqrstuvwxyz0-9-_]+!s', '', $class);

这是真的,还是有人只是对此[a-z]感到困惑\w

4

3 回答 3

13

长话短说:也许,取决于应用程序部署到的系统,取决于 PHP 是如何编译的,欢迎来到本地化和国际化的 CF。

在确定“az”的含义时,底层的 PCRE 引擎会考虑语言环境。在基于西班牙语的语言环境中,ñ 将被 az) 捕获。az 的语义是“a 和 z 之间的所有字母,ñ 在西班牙语中是一个单独的字母。

但是,PHP 盲目地将字符串处理为字节集合而不是 UTF 代码点集合的方式意味着您会遇到 az MIGHT 匹配重音字符的情况。鉴于 Drupal 部署到的各种不同系统,他们会选择明确允许的字符而不是仅仅相信 az 做正确的事情是有道理的。

我还猜想这个正则表达式的存在是关于未过滤德语变音符号的错误报告的结果。

2014 年更新:根据JimmiTh 在下面的回答,看起来(尽管有一些“令人困惑的非 pcre-core-developers”文档)[a-z]只会在abcdefghijklmnopqrstuvwxyz99% 的时间里匹配字符。也就是说——框架开发人员往往对代码中的模糊性感到不安,尤其是当代码依赖于 PHP 无法像您希望的那样优雅处理的系统(特定于语言环境的字符串)以及开发人员无法控制的服务器时。虽然匿名 Drupal 开发人员的评论是不正确的——这不是“[a-z]混淆\w”的问题,而是 Drupal 开发人员不清楚/不确定 PCRE 是如何处理的[a-z]abcdefghijklmnopqrstuvwxyz

于 2009-12-18T21:02:15.187 回答
10

Drupal 代码中的注释WRONG

" " 可能匹配是正确的。international characters (e.g. German umlauts)[a-z]

例如,如果您有可用的德语语言环境,您可以像这样检查它:

setlocale(LC_ALL, 'de_DE'); // German locale (not needed, but you never know...)
echo preg_match('/^[a-z]+$/', 'abc') ? "yes\n" : "no\n";
echo preg_match('/^[a-z]+$/', "\xE4bc") ? "yes\n" : "no\n"; // äbc in ISO-8859-1
echo preg_match('/^[a-z]+$/',  "\xC3\xA4bc") ? "yes\n" : "no\n"; // äbc in UTF-8
echo preg_match('/^[a-z]+$/u', "\xC3\xA4bc") ? "yes\n" : "no\n"; // w/ PCRE_UTF8

输出(如果替换为 不会改变de_DEde_DE.UTF-8

yes
no
no
no

字符类[abcdefghijklmnopqrstuvwxyz]与 PCRE 理解的两种编码相同[a-z]:ASCII 派生的单字节和 UTF-8(也是 ASCII 派生的)。在这两种编码[a-z]中都与[\x61-\x7A].

2009 年问这个问题的时候情况可能有所不同,但是在 2014 年没有“奇怪的配置”可以让 PHP 的 PCRE 正则表达式引擎解释[a-z]为超过 26 个字符的类(只要它[a-z]本身被写成 5 个字节)当然是 ASCII 派生的编码)。

于 2014-04-01T07:37:48.003 回答
7

只是对已经很出色但自相矛盾的答案的补充。

PCRE 库的文档始终声明“范围在字符值的整理序列中运行”。这有点模糊,但非常精确。

它是指通过PCRE内部字符表中的字符索引进行排序,可以设置为匹配当前的语言环境使用pcre_maketablestolower(i)该函数按 char 值 ( / toupper(i))的顺序构建表

换句话说,它不按实际的文化排序顺序(语言环境排序信息)进行排序。例如,虽然德语在字典排序中将 ö 与 o 视为相同,但 ö 的值使其在用于德语的所有常见字符编码(ISO-8859-x、unicode 编码等)中出现在 az 范围之外。在这种情况下,PCRE 将确定 ö 是否在[a-z]该代码值的范围内,而不是任何实际的语言环境定义的排序顺序。

PHP在他们的 docs中大部分是逐字复制PCRE 的文档。然而,他们实际上已经煞费苦心地将上述语句更改为“范围以 ASCII 整理顺序运行”。至少自 2004 年以来,该声明一直在文档中。

尽管有上述情况,但我不太确定这是真的。

好吧,至少不是在所有情况下。

PHP 发出的一个调用pcre_maketables...来自PHP 源代码

#if HAVE_SETLOCALE
    if (strcmp(locale, "C"))
        tables = pcre_maketables();
#endif

换句话说,如果编译 PHP 的环境具有setlocale 并且(LC_CTYPE) 语言环境不是 POSIX/C 语言环境,则使用运行时环境的 POSIX/C 语言环境的字符顺序。否则,将使用默认的 PCRE 表 - 在编译 PCRE 时(由pcre_maketables)生成 -基于编译器的 locale

此函数为小于 256 的字符值构建一组字符表。这些可以传递给 pcre_compile() 以覆盖 PCRE 的内部内置表(在编译 PCRE 时由 pcre_maketables() 创建)。如果您使用的是非标准语言环境,您可能需要这样做。该函数产生一个指向表的指针。

虽然德语在任何常见的字符编码中都不会有所不同[a-z],但如果我们处理 EBCDIC,例如,[a-z]将包括 ± 和 ~。当然,EBCDIC 是我能想到的一种字符编码,它不会将 az 和 AZ 置于不间断的序列中。

除非 PCRE 在使用 EBCDIC 时有一些魔力(它可能会),但除了最晦涩的 PHP 构建或运行时环境(使用您自己的、非常特殊的、定制的语言环境定义)之外,您极不可能在任何东西中包含变音符号,对于 EBCDIC,您可能会包含其他意外字符。而对于其他范围,“按 ASCII 序列排序”似乎并不完全准确。

ETA:我可以通过寻找 Philip Hazel 自己对类似问题的回复来节省一些研究:

另一个问题是字符类范围。您可能会认为 [ak] 和 [xz] 对拉丁脚本的定义很明确,但事实并非如此。

它们当然定义明确,相当于 [\x61-\x6b] 和 [\x78-\x7a],即与代码顺序有关,与文化排序顺序无关。

于 2014-04-06T10:35:51.790 回答