【问题标题】:Why does PHP's preg_quote escape unnecessary characters?为什么 PHP 的 preg_quote 会转义不必要的字符?
【发布时间】:2018-02-08 01:26:09
【问题描述】:

来自http://php.net/manual/en/function.preg-quote.php

preg_quote() 采用 str 并在作为正则表达式语法一部分的每个字符前面放置一个反斜杠。如果您有一个需要在某些文本中匹配的运行时字符串并且该字符串可能包含特殊的正则表达式字符,这将非常有用。

特殊的正则表达式字符为:. \ + * ? [ ^ ] $ ( ) { } = ! < > | : -

请注意,/ 不是特殊的正则表达式字符。

} 是不必要的,但我可以理解为什么他们会为了对称而包含它。例如。以下代码有效:

$re = '/}{This is fine}{/';
preg_match($re, $re, $match);
var_dump($match);

输出是:

array(1) {
  [0] =>
  string(16) "}{This is fine}{"
}

为什么它们包含 = ! < > :?据我所知,它们只有在被另一个未转义的元字符引入后才会变得特别,例如紧跟在(? 之后,这两个字符也会被转义。 : 在字符类中也可以是特殊的,如下所示:[[:alpha:]],但所有四个括号都会被转义。

【问题讨论】:

  • 因为总是逃避所有这些比试图找出它们可能发生的上下文要容易得多。如果您不希望它们逃脱,请不要通过preg_quote() 运行它们。
  • @Sammitch 在什么情况下不会转义它们会导致正则表达式失败?
  • 如果您的示例代码中没有特殊字符,并不意味着它根本就没有。仅供参考,从 v7.3 开始,preg_quote() 也会转义 #
  • @revo 如果他们只引用所有 [\x00-\x2F\x3A-\x40\x5B-\x60\7B-7F] 字符(我不记得下划线 [_] 的代码,去掉)。这样他们就不必担心将来更改代码。请注意,如果使用 /x 修饰符,空格 (\x20)、制表符 (\x09)、CR (\x0D) 和 LF (\x0A) 是特殊字符。
  • 转义大量字符并不简单,但已经没有想法了。当前负责此转义的 C 代码是带有 21 个cases 的switch 语句。根据PCRE_EXTENDED (x) 修饰符将# 添加到列表中。在这种模式下,几乎完全忽略空格。

标签: php regex pcre preg-quote


【解决方案1】:

我认为背后的想法是要有一致的行为。

preg_quote 的目标是为正则表达式模式生成文字字符串。这意味着无论上下文如何,返回的字符串中的任何字符都不能被解释为自身以外的其他内容,并且上下文可以是与模式的其他部分的串联。

如果我写'/(?' . preg_quote('>') . 'abc)/',我希望> 不会被解释为原子组的>,并且该模式会返回错误。

如果我写'/.{3' . preg_quote('}') . '/',我希望} 不会被解释为量词的右大括号,并且该模式匹配像'a{3}' 这样的字符串,但不匹配'abc'

您可以使用前瞻断言、命名组、非捕获组或原子组轻松为 = ! < > : 构建相同类型的示例。

重要的是,无论使用函数的方式或上下文如何,预期的行为总是相同的。

【讨论】:

  • 你的例子很糟糕。如果{ 不应该是元,它应该在模式中转义。
  • @CJDennis:不。{ isnt' 在我的示例中应该是任何东西,元或不是元。问题是你总是考虑工作模式而不考虑错误模式和边缘情况。此外,左大括号是 pcre 中的一种特殊情况,因为它仅在上下文中成为元数据:/.{3\}/ 匹配 'a{3}'/.{abc}/ 匹配 'z{abc}' 没有错误。我将选择列表中的另一个字符作为示例。
  • @cale_b:你说得对,这很难理解,我会改变它。
  • 代码的作者要么打算将其作为元数据,要么不打算将其作为元数据(可能他们忘记转义并使其成为文字)。没有“都不是”的第三个选项。模式/.{3\}/ 是有效的,将匹配任何单个字符加上{3} 的文字字符串,例如a{3}.
  • @CJDennis:一个函数不关心作者的意图,它的工作是在任何情况下都返回相同的答案,preg_quote 的目标是中和所有可能的字符被视为特殊序列的一部分。在我的示例中,也没有作者,没有意图,我仅说明此函数的预期行为。
【解决方案2】:

如果您尝试编写这样的代码会发生什么:

$lookahead = getUserInput();  // Not escaped
$results = preg_match('/abc(?' . $lookahead . ')/', $subject);

用户输入!def?答案是你得到否定的前瞻而不是常规的前瞻。如果您不想允许负前瞻,则需要确保感叹号被转义。

【讨论】:

  • 您没有preg_quote() 用户输入,即使您输入了,(!=def) 除了捕获括号外也没有什么特别之处。它将匹配abc!=def 的任何实例,将capture[0] 设置为abc!=def 并将capture[1] 设置为!=def。如果您在用户输入的开头添加?,它将被引用并且仅匹配文字问号。
  • @CJDennis 是的,关键是要演示当你不preg_quote() 时会发生什么。这就是为什么preg_quote() 会转义所有有效的正则表达式符号,而不仅仅是在最外层上下文中有效的符号。
  • 请再次阅读我的问题。我没有问preg_quote()的目的是什么,我问为什么有些字符被不必要地引用了。例如。有人想引用@ 符号,因为他们处理电子邮件地址。由于@ 从来都不是特别的,所以它不需要被引用,但. 可以。
  • @CJDennis 我用它来演示一个示例,说明为什么可能需要转义这些不寻常的字符。感叹号用于负前瞻和后视。尖括号用于命名捕获。冒号用于命名字符类。并且符号 (@) 不会被转义。 (在 PCRE 源代码中自己查看:raw.githubusercontent.com/php/php-src/master/ext/pcre/…)但它在 perl 中被转义(从它的名称中提取),因为它是 perl 的数组标记。
猜你喜欢
  • 1970-01-01
  • 2019-08-20
  • 1970-01-01
  • 1970-01-01
  • 2018-04-26
  • 1970-01-01
  • 2020-08-30
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多