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

时间:2011-03-18 21:28:00

标签: php regex

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

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);

我逃过了美元符号,所以给出了什么?

2 个答案:

答案 0 :(得分:37)

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

$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

答案 1 :(得分:6)

我知道这个问题有点旧,但我在试图找到同样问题的答案时发现了这个问题。我看到它处于搜索引擎排名的顶端,所以我认为解释一个简单的替代方案会很好,为什么会出现双引号字符串( " )

我使用的正则表达式中包含大量单引号字符( ' ),所以我不太热衷于用它们包装表达式,因为我不想逃避所有这些。< / p>

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

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

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

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

通过“双重转义”美元符号,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'

会将\$发送到regex,与双引号字符串中的\\\$相同。

所有这些都取决于您使用哪种风格的个人偏好,或者哪种风格更容易。

TL; DR:对\$等单引号字符串使用'/Hello \$bob/is',对\\\$等双引号字符串使用"/Hello \\\$bob/is"