php 中贪婪匹配 与 惰性匹配
-
贪婪匹配:尽可能多的匹配多的字符
- 比如 正则表达式 "m.*n" 它将匹配最长以m开始,n结尾的字符串。
- 如果用它来搜索manmpndegenc的话,它将匹配到的字符串是manmpndegen而非man。
- 可以这样想,当匹配到m的时候,它将从后面往前匹配字符n。
- 贪婪匹配 尽可能匹配多字符
-
惰性匹配:尽可能少的匹配字符
- 将一个贪婪匹配转为惰性匹配呢?只需要在其后面添加一个"?"即可
- 如"m.*?n"将匹配manmpndegenc,匹配到的字符串是man。
-
示例
/*
* preg_match 执行一个正则表达式匹配
* int preg_match(string $pattern, string $subject [, array &$matches [, int $flags = 0, int $offset = 0]])
* $pattern 要搜索的模式
* $subject 输入字符串
* $matches 搜索结果 $matches[0]将包含完整模式匹配到的文本 $matches[1]将包含第一个捕获子组匹配到的文本
* $flags 标记:PREG_OFFSET_CAPTURE 如果传递了这个标记,对于每一个出现的匹配返回时会附加字符串偏移量(相对于目标字符串的)。
* 注意:这会改变填充到matches参数的数组,
* 使其每个元素成为一个由第0个元素是匹配到的字符串,
* 第1个元素是该匹配字符串在目标字符串subject中的偏移量
* $offset 搜索从目标字符串的开始位置开始。指定从目标字符串的某个未知开始搜索(单位是字节)
*/
$m = array();
$string = "manmpndegenc";
// 贪婪模式
preg_match("|m.*n|",$string,$m); // $m => [0 => "manmpndegen"]
// 惰性模式
preg_match("|m.*?n|",$string,$m); // $m => [0 => "man"]
dump($m);
函数符 | 描述 |
---|---|
*? | 零次或多次,但尽可能少的匹配 |
+? | 一次或多次,但尽可能少的匹配 |
?? | 零次或壹次,但尽可能少的匹配 |
{n,}? | 至少n次,但尽可能少的匹配 |
{n,m}? | n到m次,但尽可能少的匹配 |
php正则表达式 回溯与固态分布
回溯
- 回溯就像是在走岔路口,当遇到岔路的时候就先在每个路口做一个标记。
- 如果走了死路,就可以照原路返回,直到遇见之前所做过的标记,标记着还未尝试过的道路。
- 如果那条路也走不能,可以继续返回,找到下一个标记,如此重复,直到找到出路,或者直到完成所有没有尝试过的路。
$str='aageacwgewcaw';
$pattern='/a\w*c/i';
$str=preg_match($pattern, $str);
// 匹配$str是否包含这样一个由”a+0个或多个字母+c”不区分大小写的字符串。
// 但是至于程序怎样去匹配的呢?匹配的过程中,回溯了多少次呢?
$mat = array();
// \w 匹配 字母(大写/小写)数字下划线
$str='aageacwgewcaw';
$pattern='/a\w*c/i';
// aageac 惰性匹配
// aageacwgewc 贪婪匹配
preg_match($pattern, $str, $mat);
/*
* array:1 [
* 0 => "aageacwgewc"
* ]
*/
dump($mat);
匹配过程 | 接下来操作描述 |
---|---|
"a\w*c"中a匹配到"aageacwgewcaw"中第一个字符a | \w进行下一个字符匹配 |
\w是贪婪匹配,会一直匹配到"aageacwgewcaw"中最后一个字符w | c进行下一个字符匹配时 |
"a\w*c"中c发现没有可以匹配的 | 于是\w匹配进行第一次回溯,匹配到倒数第二个字符a |
"a\w*c"中c发现还是没有可以匹配的 | 于是\w匹配进行第二次回溯,匹配到倒数第三个字符c |
"a\w*c"中c匹配成功 | 匹配介绍返回结果 |
- 如果我们将pattern改为pattern="/a\w*?c/i";又会回溯多少次呢?正确答案是回溯三次。
- 匹配过程 "aageacwgewcaw"
匹配过程 | 备注 | 下部操作 |
---|---|---|
"a\w*?c" => aag 匹配失败 | 回溯到 "aa" | \w => ag |
"a\w*?c" => aage 匹配失败 | 回溯到 "aag" | \w => age |
"a\w*?c" => aagea 匹配失败 | 回溯到 "aage" | \w => agea |
"a\w*?c" => aageac 匹配成功 | ---- | ---- |
固态分组
- 固态分组:减少回溯次数 (注意下面加粗字符)
- 使用(?>...)括号中的匹配如果产生了备选状态,那么一旦离开括号便会被立即 引擎抛弃掉。
- 例子:
- \w+:’这个表达式在进行匹配时的流程是这样的,会优先去匹配所有的符合\w的字符。
- 假如字符串的末尾没有’:’,即匹配没有找到冒号,此时触发回溯机制,他会迫使前面的\w+释放字符,并且在交还的字符中重新尝试与’:’作比对。
- 但是问题出现在这里: \w是不包含冒号的,显然无论如何都不会匹配成功,可是依照回溯机制,引擎还是得硬着头皮往前找,这就是对资源的浪费。
- 所以我们就需要避免这种回溯,对此的方法就是将前面匹配到的内容固化,不令其存储备用状态!,那么引擎就会因为没有备用状态可用而只得结束匹配过程。大大减少回溯的次数。
- 备注 \w 匹配 字母(大小写)数字下划线
$m = array();
$str = "nihaoaheloo";
$pattern = "/(?>\w+):/";
preg_match($pattern, $str, $m);
// []
dump($m);
- 当然有些时候,又需慎用固态分组,如下,我要检查$str中是否包含以a结尾的字符串,很明显是包含的,但是因为使用了固态分组,反而达不到我们想要的效果。
$str='nihaoahelaa';
$pattern1='/(?>\w+)a/';
$pattern2='/\w+a/';
$rs=preg_match($pattern1, $str);//0
$rs=preg_match($pattern2, $str);//1
php 正则 (?:pattern) 作用
$rule1 = "/(?:\w+)/"; // 匹配,则捕获整体,不会捕获子组
$rule2 = "/(\w+)/"; // 匹配,则捕获整体,并捕获子组
preg_match($rule1, "Hi", $matches);
preg_match($rule2, "Hi", $matches2);
/**
* array:1 [
* 0 => "Hi"
* ]
*/
dump($matches);
/**
* array:2 [
* 0 => "Hi"
* 1 => "Hi"
* ]
*/
dump($matches2);
- 作用: (|) 来组合一个模式有用,如, 'industr(?:y|ies) 就是一个比 'industry|industries' 更简略的表达式。
参考文档:
php中正则表达式详解 | 作者:helloworldlee | 链接:https://www.cnblogs.com/hellohell/p/5718319.html
网友评论