其他示例
参考答案已经解释了“未知修饰符”警告的原因。这只是其他典型变体的比较。
-
当忘记添加正则表达式分隔符时,第一个非字母符号将被假定为一个。因此,警告通常是关于分组之后的内容,元符号:/
/
(…)
[…]
preg_match("[a-zA-Z]+:\s*.$"
↑ ↑⬆
-
有时,您的正则表达式已经使用了自定义分隔符(此处),但仍包含与未转义文本相同的字符。然后它被误认为是不成熟的分隔符。这就是为什么下一个符号收到“未知修饰❌符”奖杯的原因::
preg_match(":\[[\d:/]+\]:"
↑ ⬆ ↑
-
使用经典分隔符时,请注意不要将其按字面意思放在正则表达式中。在尝试匹配未转义的文件名时,最常发生这种情况:/
preg_match("/pathname/filename/i"
↑ ⬆ ↑
或者在匹配角度/方括号样式标签时:
preg_match("/<%tmpl:id>(.*)</%tmpl:id>/Ui"
↑ ⬆ ↑
-
模板样式(Smarty 或 BBCode)正则表达式模式通常需要或方括号。两者通常都应该转义。(不过,最外层的一对是例外)。{…}
[…]
{}
当不使用实际的分隔符时,它们也会被误解为成对的分隔符。如果它们也被用作内部的文字字符,那么当然......错误。
preg_match("{bold[^}]+}"
↑ ⬆ ↑
-
每当警告说“分隔符不能是字母数字或反斜杠”时,您也完全忘记了分隔符:
preg_match("ab?c*"
↑
-
"Unkown 修饰符 'g'“ 通常表示从 JavaScript 或 Perl 逐字复制的正则表达式。
preg_match("/abc+/g"
⬆
PHP 不使用全局标志。相反,preg_replace
函数适用于所有发生,preg_match_all
是一次性preg_match
的“全局”搜索吊坠。/g
因此,只需删除该标志即可。/g
另请参见:
·警告: preg_replace(): 未知修饰符 “g”
·preg_replace:错误的正则表达式==“未知修饰符”?
-
一个更奇怪的情况是PCRE_EXTENDED /x
标志。这通常(或应该)用于使正则表达式更加崇高和可读。
这允许使用内联注释。PHP 在 PCRE 之上实现正则表达式分隔符。但它没有以任何特殊的方式对待。这就是注释中的文字分隔符如何成为错误:#
#
#
preg_match("/
ab?c+ # Comment with / slash in between
/x"
(同样值得注意的是,用作分隔符可能是双重不可取的。#
#abc+#x
-
将变量插值到正则表达式中需要对它们进行预转义,或者它们本身是有效的正则表达式。您无法事先判断这是否有效:
preg_match("/id=$var;/"
↑ ↺ ↑
在这种情况下,最好应用 $var = preg_quote($var, “/”)。
另请参见:
·未知修饰符“/”在...?它是什么?
另一种方法是对未加引号的文字字符串使用转义:\Q…\E
preg_match("/id=\Q{$var}\E;/mix");
请注意,这只是元符号的便捷快捷方式,不可靠/安全。如果包含文字本身(尽管不太可能),它会分崩离析。它不会掩盖分隔符本身。$var
'\E'
不推荐使用的修饰符 /e 是一个完全不同的问题。这与分隔符无关,但隐式表达式解释模式正在逐步淘汰。另请参见:将已弃用的 preg_replace /e 替换为 preg_replace_callback
替代正则表达式分隔符
如前所述,此错误的最快解决方案只是选择一个不同的分隔符。可以使用任何非字母符号。视觉上独特的通常是首选:
~abc+~
!abc+!
@abc+@
#abc+#
=abc+=
%abc+%
从技术上讲,您可以使用 或 分隔符。但是,最好避免使用本身用作正则表达式元字符的符号。$abc$
|abc|
哈希作为分隔符也相当流行。但应注意与 / 可读性修饰符结合使用。你不能使用或注释,因为它们会被混淆为分隔符。#
x
PCRE_EXTENDED
# inline
(?#…)
仅引号分隔符
有时,您会看到并用作正则表达式分隔符,并将它们的 conterpart 作为 PHP 字符串外壳配对:"
'
preg_match("'abc+'"
preg_match('"abc+"'
就PHP而言,这是完全有效的。它有时方便且不显眼,但在IDE和编辑器中并不总是清晰可辨。
成对分隔符
一个有趣的变体是成对的分隔符。您可以使用任何方括号/大括号组合,而不是在正则表达式的两端使用相同的符号。<...>
(...)
[...]
{...}
preg_match("(abc+)" # just delimiters here, not a capture group
虽然它们中的大多数也用作正则表达式元字符,但您通常无需进一步努力即可使用它们。只要正则表达式中的那些特定大括号/帕伦正确配对或转义,这些变体就非常可读。
花哨的正则表达式分隔符
一个有点懒惰的技巧(特此不认可)是使用不可打印的ASCII字符作为分隔符。这在 PHP 中很容易工作,方法是对正则表达式字符串使用双引号,对分隔符使用八进制转义:
preg_match("\001 abc+ \001mix"
只是一个通常不需要的控制字符。因此,它极不可能出现在大多数正则表达式模式中。这使得它适合这里,即使不是很清晰。\001
␁
可悲的是,你不能使用Unicode glyps作为分隔符。PHP 只允许单字节字符。这是为什么呢?好吧,很高兴你问:❚
PCRE 顶部的 PHPs 分隔符
preg_*
函数利用 PCRE 正则表达式引擎,该引擎本身并不关心或提供分隔符。为了与Perl相似,函数实现了它们。这也是为什么您可以使用修饰符字母 /ism
而不仅仅是常量作为参数的原因。preg_*
请参阅 ext/pcre/php_pcre.c 了解如何预处理正则表达式字符串:
首先,忽略所有前导空格。
-
任何非字母数字符号都被视为假定的分隔符。请注意,PHP 仅支持单字节字符:
delimiter = *p++;
if (isalnum((int)*(unsigned char *)&delimiter) || delimiter == '\\') {
php_error_docref(NULL,E_WARNING, "Delimiter must not…");
return NULL;
}
正则表达式字符串的其余部分从左到右遍历。仅忽略反斜杠转义符号。\Q
和 \E
转义不被接受。\\
如果再次找到分隔符,则验证其余部分仅包含修饰符字母。
-
如果分隔符是可配对的大括号/方括号之一,则处理逻辑更加精细。([{< )]}> )]}>
int brackets = 1; /* brackets nesting level */
while (*pp != 0) {
if (*pp == '\\' && pp[1] != 0) pp++;
else if (*pp == end_delimiter && --brackets <= 0)
break;
else if (*pp == start_delimiter)
brackets++;
pp++;
}
它查找正确配对的左右分隔符,但在计数时忽略其他大括号/括号类型。
只有在分隔符和修饰符标志被剪切掉之后,原始正则表达式字符串才会传递到 PCRE 后端。
现在这一切都有点无关紧要。但解释了分隔符警告的来源。这整个过程都是为了具有最低限度的Perl兼容性。当然,也有一些小的偏差,比如字符类上下文在PHP中没有得到特殊处理。[…]
更多参考资料