迹忆客 专注技术分享

当前位置:主页 > 学无止境 > 编程语言 >

正则表达式中的那些模式修饰符(二)

作者:迹忆 最近更新:2021/03/26 浏览次数:

e (PREG_REPLACE_EVAL)

该修饰符已经被PHP7版本弃用了。如果设置了修饰符, preg_replace() 在进行了对替换字符串的 后向引用替换之后, 将替换后的字符串作为php 代码评估执行(eval 函数方式),并使用执行结果 作为实际参与替换的字符串。单引号、双引号、反斜线()和 NULL 字符在 后向引用替换时会被用反斜线转义。

<?php

$str = "<p>This is an example</p>";
$pattern = "/^<p>(.*)<\/p>$/e";
$str = preg_replace($pattern,"add_tr('\\1')",$str);
var_dump($str);
function add_tr($str)
{
    return "<tr>$str</tr>";
}

这里要使用 php5+ 版本来执行,而不能使用php7 版本。执行结果

string(27) "<tr>This is an example</tr>"

注意,对于 php5 高版本的会出现提示说该修饰符已经被降级

PHP Deprecated:  preg_replace(): The /e modifier is deprecated, use preg_replace_callback instead in /Users/liuhanzeng/workspace/php/reg.php on line 7
Deprecated: preg_replace(): The /e modifier is deprecated, use preg_replace_callback instead in /Users/liuhanzeng/workspace/php/reg.php on line 7
string(27) "<tr>This is an example</tr>"

如果不指定e $pattern = '/^<p>(.*)<\/p>$/' 那结果就成了 add_tr('This is an example')。也就是不能使用回调函数了,直接当成字符串去把目标字符串替换掉。

如果说设置了e之后是使用回调函数的话,这种说法是不正确的。而是将preg_replace函数的第二个参数使用eval()函数来执行,然后将执行后的结果替换掉目标字符串。 下面再举几个例子来说明

<?php

$str = "<p>This is an example</p>";
$pattern = "/^<p>(.*)<\/p>$/e";
$str = preg_replace($pattern,"\\1",$str);

当匹配成功以后,\\1是捕获的第一个子组的内容——This is an example。 设置了e,就会将\\1eval函数执行。所以就会报错

Fatal error: preg_replace(): Failed evaluating code:
This is an example in ...

因为 This is an example 不是一段有效的PHP代码。

注意:因为我们在preg_replace中给的第二个参数是\\1,所以在eval看来This is an example 并不是一个字符串。如果我们给的第二个参数是 '\\1',那在eval看来是这样的'This is an example'。所以就不会报错。 看例子

<?php
$str = "<p>This is an example</p>";
$pattern = "/^<p>(.*)<\/p>$/e";
$str = preg_replace($pattern,"'\\1'",$str);
var_dump($str);

正确替换了目标字符串

string(18) "This is an example"

所以说我们回过头来再看下面这个例子

<?php

$str = "<p>This is an example</p>";
$pattern = "/^<p>(.*)<\/p>$/e";
$str = preg_replace($pattern,"add_tr('\\1')",$str);
var_dump($str);
function add_tr($str)
{
    return "<tr>$str</tr>";
}

仔细看的话发现这种形式并不是严格意义上的指定一个回调函数,它就是一个对函数的调用代码。因为通常的指定回调函数是不能加小括号()的。

由于ePHP7中已经被废弃了,所以要想使用上面那种回调函数,可以用preg_replace_callback来代替。

<?php

$str = "<p>This is an example</p>";
$pattern = "/^<p>(.*)<\/p>$/";
$str = preg_replace_callback($pattern,function ($matches) {
    return add_tr($matches[1]);
},$str);
var_dump($str);
function add_tr($str)
{
    return "<tr>$str</tr>";
}

使用PHP7执行结果就正常了

string(27) "<tr>This is an example</tr>"

注意:在function($matches){}中最后一定要有return

通过上面preg_replace_callback的回调函数,然后对比之前例子中使用e指定的回调函数,发现形式是不是不一样的。

其实,是不是可以认为e就是使用的eval的第一个字符。就是为了对preg_replace第二个参数使用eval函数执行。由此也知道,e只是在使用preg_replace函数的时候设置,其他函数和它就没有关系了。

由于eval函数是存在很大的风险的,容易造成远程执行任何代码。所以不推荐使用。 这也是为什么废弃它的原因。

A (PCRE_ANCHORED)

如果设置了这个修饰符,模式被强制为"锚定"模式,也就是说约束匹配使其仅从目标字符串的开始位置搜索。这个效果同样可以使用适当的模式构造出来,并且 这也是 perl 种实现这种模式的唯一途径。

<?php

$str = "<br><a>This is a href</a>";

// 不指定 A
$pattern = '/<a>([^<]+)<\/a>$/';

// 指定 A
$pattern_A = '/<a>([^<]+)<\/a>$/A';

$res = preg_match($pattern,$str,$matches);
$res_A = preg_match($pattern,$str,$matches_A);

print_r($matches);
print_r($matches_A);

不指定A,可以匹配到。 指定A之后,相当于是强制将 <a>从目标字符串的开始位置匹配,而目标字符串的开始位置为<br> ,所以匹配不到。

// 不指定 `A`
Array
(
    [0] => <a>This is a href</a>
    [1] => This is a href
)

// 指定 `A` 
Array
(
)

注意: 这个修饰符的作用和 ^ 很像。不同的是 ^ 是指定行首的位置,当然包括目标字符串的开始位置,它是受修饰符m和字符串中的换行符\n 的影响的。 而 A 是强制整个目标字符串的开始位置。

D (PCRE_DOLLAR_ENDONLY)

如果这个修饰符被设置,模式中的元字符 $ 仅仅匹配目标字符串的末尾。当字符串以一个换行符结尾时,如果这个修饰符没有设置, $不会匹配该换行符;如果这个修饰符被设置,模式中的元字符 $ 仅仅匹配目标字符串的末尾,会去匹配末尾的这个换行符\n。 如果设置了修饰符m,这个修饰符被忽略。在 perl 中没有与此修饰符等同的修饰符。其实,总结来说D是控制$是否匹配目标字符串的结尾的换行符\n的(注意,这里是整个目标字符串)。前面在介绍修饰符m的时候说过,设置了m之后,会根据\n对目标字符串进行分行,$ 是匹配每行的末尾。所以说如果设置了m, 那么D的作用就消失了。因此D不能和m同时设置。

<?php

$str = "<p>This is not \n an example</a>\n";

// 不设置 D
$pattern = '/<p>([^<]+)<\/a>$/';
$res = preg_match($pattern,$str,$matches);
print_r($matches);

在没有设置D的情况下,$不会去匹配末尾的换行符\n。 所以上面的正则匹配不到任何内容。

Array
(
    [0] => <p>This is not an example</a>
    [1] => This is not an example
)

设定 D之后,就不会忽略末尾的换行符

<?php

$str = "<p>This is not \n an example</a>\n";

// 不设置 D
$pattern = '/<p>([^<]+)<\/a>$/D';
$res = preg_match($pattern,$str,$matches);
print_r($matches);

匹配不到内容

Array
(
)

中文官网中对这个修饰符的解释貌似有错误,把它的作用说反了。有可能是翻译的时候它理解错了;也有可能是我没有理解中文所表述的或者我对英文版的解释理解错了。 但是程序的执行结果告诉我,我理解的和程序的执行结果是一致的。

S

当一个模式需要多次使用的时候,为了得到匹配速度的提升,值得花费一些时间 对其进行一些额外的分析。如果设置了这个修饰符,这个额外的分析就会执行。当前, 这种对一个模式的分析仅仅适用于非锚定模式的匹配(即没有单独的固定开始字符)。

除非注明转载,本站文章均为原创或翻译,欢迎转载,转载请以链接形式注明出处

本文地址:

迹忆客

专注技术分享,项目实战分享!

技术宅 乐于分享 7年编程经验
社交账号
  • https://www.github.com/onmpw
  • qq:1244347461

热门文章

教程更新

热门标签