当模式涉及美元符号 ($) 时,正则表达式失败

2022-08-30 18:55:37

当涉及到涉及美元符号的匹配子模式时,我遇到了一些问题。例如,请考虑以下文本块:

Regular Price: $20.50       Final Price: $15.20
Regular Price: $18.99       Final Price: $2.25
Regular Price: $11.22       Final Price: $33.44
Regular Price: $55.66       Final Price: $77.88

我试图将常规/最终价格集与以下正则表达式相匹配,但它根本不起作用(根本没有匹配):
preg_match_all("/Regular Price: \$(\d+\.\d{2}).*Final Price: \$(\d+\.\d{2})/U", $data, $matches);

我逃脱了美元符号,那么什么给了?


答案 1

在双引号字符串中,反斜杠被视为 的转义字符。PHP 解析器甚至在函数看到它之前就删除了反斜杠:$preg_match_all

$r = "/Regular Price: \$(\d+\.\d{2}).*Final Price: \$(\d+\.\d{2})/U";
var_dump($r);

输出(ideone):

"/Regular Price: $(\d+\.\d{2}).*Final Price: $(\d+\.\d{2})/U"
                 ^                           ^
              the backslashes are no longer there

要解决此问题,请使用单引号字符串而不是双引号字符串:

preg_match_all('/Regular Price: \$(\d+\.\d{2}).*Final Price: \$(\d+\.\d{2})/U',
               $data,
               $matches);

看到它在线工作:ideone


答案 2

我知道这个问题有点老了,但我在试图找到同一问题的答案时发现了这个问题。我看到它在搜索引擎排名中名列前茅,所以我认为最好解释一个简单的替代方案,以及为什么双引号字符串会发生这种情况。( " )

我使用的正则表达式包含大量单引号字符,因此我不太热衷于用它们包装表达式,因为我不想转义所有这些字符。( ' )

我的解决方案是“双重逃逸”美元符号。在您的示例中,它看起来应类似于

"/Regular Price: \\\$(\d+\.\d{2}).*Final Price: \\\$(\d+\.\d{2})/U";

请注意,美元符号现在包含 3 个斜杠 。\\\

基本上,我们有两个“层次”的解释,PHP的解释和正则表达式的解释。发生的事情是,使用一个斜杠,PHP将其解释为文字字符而不是变量修饰符,因此它吃掉斜杠,解释Mark答案中概述的字符串,然后将其发送到正则表达式,正则表达式解释为后看。

通过“双重转义”美元符号,PHP 分别解释为 和。我们从第一组字符中转义,并从第二组字符转义,导致在 PHP 解释之后。这将发送文本字符串\\\$\\\$\$\$

"/Regular Price: \$(\d+\.\d{2}).*Final Price: \$(\d+\.\d{2})/U";

到正则表达式,它将解释为字符文字 ,它将匹配而不是充当后面的外观,因为它被转义了。在这里实现双层解释很重要,因为PHP和正则表达式都有自己的解释规则,并且可能需要最多4个斜杠才能正确转义字符。\$$$

单引号字符串没有这个问题,因为要在字符串中使用变量,我们必须编写$foo

'Hello '. $foo .'!';

而不是

"Hello $foo!";

就像我们可以在双字符串中一样。与双引号字符串不同,单引号字符串不能将字符串内的变量解释为变量(除非像上面的示例那样追加它们),而是将它们解释为纯文本。由于我们不必再逃避变量,因此我们可以逃脱

'/Regular Price: \$(\d+\.\d{2}).*Final Price: \$(\d+\.\d{2})/U'

这将发送到正则表达式,与双引号字符串中的相同。\$\\\$

这完全取决于您使用哪种样式的个人偏好,或者哪种样式对于模式来说更容易。

TL;DR:用于单引号字符串(如 ,)和双引号字符串(如 )。\$'/Hello \$bob/is'\\\$"/Hello \\\$bob/is"


推荐