[a-z]会不会匹配PREG / PCRE中的重音字符?

时间:2009-12-18 20:24:05

标签: php regex ascii pcre

我已经知道PCRE中的\w(特别是PHP的实现)有时可以匹配某些非ASCII字符,具体取决于系统的区域设置,但是[a-z]呢?

我不这么认为,但我注意到Drupal的一个核心文件中的这些行(包括/ 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混淆了?

3 个答案:

答案 0 :(得分:13)

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

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

然而,PHP盲目处理字符串作为字节集合而不是UTF代码点集合的方式意味着你有一种情况,其中a-z MIGHT匹配重音字符。鉴于Drupal被部署到不同系统的各种各样,有意义的是他们会选择明确允许的字符而不仅仅是信任a-z来做正确的事情。

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

2014年更新:每JimmiTh's answer below,看起来(尽管有一些“令人困惑的非pcre-core-developers”文档)[a-z]只会匹配字符abcdefghijklmnopqrstuvwxyz 99%的时间是众所周知的。也就是说 - 框架开发人员往往会对代码中的模糊性感到不安,特别是当代码依赖于PHP无法正常处理的系统(特定于语言环境的字符串)时,以及开发人员无法控制的服务器。虽然匿名Drupal开发人员的评论不正确 - 这不是“让[a-z]\w混淆”,而是Drupal开发人员不清楚/不确定PCRE如何处理[a-z] ,并选择更具体的abcdefghijklmnopqrstuvwxyz形式,以确保他们想要的特定行为。

答案 1 :(得分:10)

Drupal代码中的评论错误

,“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_DE替换为de_DE.UTF-8,则不会更改):

yes
no
no
no

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

在2009年提出问题时可能会有所不同,但在2014年没有“奇怪的配置”可以使PHP的PCRE正则表达式引擎将[a-z]解释为超过26个字符的类(只要很长因为[a-z]本身在ASCII派生编码中被写为5个字节,当然。)

答案 2 :(得分:7)

只是对已经很好的,如果相互矛盾的答案的补充。

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

它指的是通过PCRE内部字符表中的字符的索引进行整理,可以使用pcre_maketables将其设置为与当前语言环境匹配。该函数按char值(tolower(i) / toupper(i)

的顺序构建表

换句话说,它不会按实际文化排序顺序(区域设置排序规则信息)进行整理。例如,虽然德语在字典整理中对待o与o相同,但是ö的值使得它在用于德语的所有常用字符编码(ISO-8859-x,unicode编码等)中显示在az范围之外。在这种情况下,PCRE将确定ö是否在该代码值的[a-z]范围内,而不是任何实际的区域设置定义的排序顺序。

PHP主要在PCRE's documentation中逐字复制their docs。然而,他们实际上已经痛苦地将上述陈述改为“Ranges以ASCII整理顺序运行”。自2004年以来,该声明一直在文档中。

尽管如此,我并不确定这是真的。

嗯,至少在所有情况下都不是。

PHP对pcre_maketables的一次调用......来自PHP source

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

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

  

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

虽然德语在任何常见字符编码中与[a-z]没有区别,但如果我们处理EBCDIC,例如[a-z]将包含±和〜。当然,EBCDIC是我能想到的一种字符编码,它不会将a-z和A-Z置于不间断的序列中。

除非PCRE在使用EBCDIC时有一定的魔力(并且它可能),尽管你不太可能将变音符号包含在最模糊的PHP构建或运行时环境中(使用你自己的,非常特殊的,定制的)语言环境定义),对于EBCDIC,您可能包括其他非预期字符。而对于其他范围,“以ASCII序列整理”似乎并不完全准确。

ETA:我本可以通过寻找Philip Hazel对类似问题的回复来保存一些研究成果:

  
    
      

另一个问题是字符类范围。你会认为[a-k]和[x-z]是为拉丁文脚本定义的,但情况并非如此。

    
  
     

它们当然定义得很好,等同于[\ x61- \ x6b]和[\ x78- \ x7a],即与代码顺序相关,而不是文化排序顺序。