PHP 正则表达式中什么时候需要 u-modifier?

When do I need u-modifier in PHP regex?

我知道,PHP PCRE 函数将字符串视为字节序列,因此许多站点建议使用 /u 修饰符来处理输入和正则表达式作为 UTF-8。

但是,我真的总是需要这个吗?我的测试表明,当我不使用转义序列或点或类似的东西时,这个标志没有任何区别。

例如

preg_match('/^[\da-f]{40}$/', $string); 检查字符串是否具有 SHA1 散列格式

preg_replace('/[^a-zA-Z0-9]/', $spacer, $string); 替换非 ASCII 字母或数字的每个字符

preg_replace('/^\+\((.*)\)$/', '', $string); 用于获取 +(XYZ)

的内部内容

这些正则表达式仅包含单字节 ASCII 符号,因此它应该适用于 每个 输入,无论编码如何,不是吗?请注意,第三个正则表达式使用点运算符,但由于我在字符串的开头和结尾切断了一些 ASCII 字符,这应该也适用于 UTF-8,对吗?

如果我忽略了什么,没有人能告诉我吗?

u (PCRE_UTF8)
This modifier turns on additional functionality of PCRE that is incompatible with Perl. Pattern and subject strings are treated as UTF-8. An invalid subject will cause the preg_* function to match nothing; an invalid pattern will trigger an error of level E_WARNING. Five and six octet UTF-8 sequences are regarded as invalid since PHP 5.3.4 (resp. PCRE 7.3 2007-08-28); formerly those have been regarded as valid UTF-8.

当您必须比较 Unicode 字符(例如韩语或日语)时,您将需要它。

换句话说,除非你不是在比较非 Unicode 的字符串(例如英文),否则你不需要使用这个标志。

第一个表达式没有问题。被量化的字符是明确的单字节字符,不能出现在 UTF-8 多字节序列中。

第二个表达式可能会给您提供比您预期更多的间隔符;例如:

echo preg_replace('/[^a-zA-Z0-9]/', "0", "");
// => 0000

第三个表达式也没有问题,因为重复字符受括号限制(这是 ASCII 安全的)。

这个比较危险:

echo preg_replace('/^(.)/', "0", "");
// => 0???

通常,在不了解更多有关 UTF-8 工作原理的情况下,可能很难预测哪些正则表达式是安全的,哪些不安全,因此对可能包含高于 U 的字符的所有文本使用 /u +007F 是最佳实践。

Unicode 修饰符 u 允许正确检测始终为多字节的重音字符。

preg_match('/([\w ]{2,})/', 'baz báz báž', $match); 
// $match[0] = "baz b" ... wrong, accented/multibyte chars silently ignored

preg_match('/([\w ]{2,})/u', 'baz báz báž', $match); 
// $match[0] = "baz báz báž" ... correct

也可以使用它来安全检测空格:

preg_replace(''/\s+/u', ' ', $txt); // works reliably e.g. with EOLs (line endings)