我对正则表达式相当陌生,并且被困在下面,我尝试使用preg_match_all
来计算世界之后的hello数量。
如果我使用"world".+(hello)
,它计入最后一个问候;"world".*?(hello)
在第一个问候中停止,都给出了一个计数。
blah blah blah
hello
blah blah blah
class="world"
blah blah blah
hello
blah blah
hello
blah blah blah
hello
blah blah blah
我期待3
作为计数,因为world
之前的hello
不应该被计算在内。
使用简单正则表达式的另一个选项:
if(preg_match('/"world".*/s', $str, $out)) {
echo preg_match_all('/bhellob/', $out[0]);
}
在tio.run上观看演示
您可以在此处使用单个preg_match_all
调用:
$text = "blah blah blahnhellonblah blah blahnclass="world" nblah blah blahnhello nblah blahnhellonblah blah blahnhellonblah blah blah";
echo preg_match_all('~(?:G(?!^)|bworldb).*?Kbhellob~s', $text);
请参阅正则表达式演示和 PHP 演示。详情:
(?:G(?!^)|bworldb)
- 前一场比赛的结束(G(?!^)
执行此检查:G
匹配字符串的开头或前一匹配位置的结束,因此我们需要排除字符串位置的开始,这是通过(?!^)
负前瞻完成的)或整个单词world
.*?
- 任何零个或多个字符,尽可能少K
- 丢弃到目前为止匹配的所有文本bhellob
- 一个完整的单词hello
.
注意:如果不需要字边界检查,则可以从模式中删除b
。
如果hello
和world
是用户定义的模式,则必须在模式中preg_quote
它们:
$start = "world";
$find = "hello";
$text = "blah blah blahnhellonblah blah blahnclass="world" nblah blah blahnhello nblah blahnhellonblah blah blahnhellonblah blah blah";
echo preg_match_all('~(?:G(?!^)|' . preg_quote($start, '~') . 'b).*?K' . preg_quote($find, '~') . '~s', $text);
其他方法:强制模式失败,如果字符串中不存在world
则不重试:
~(?:A(*COMMIT).*?world)?.*?hello~s
演示
非捕获组是可选的,但贪婪。因此,每次尝试该模式时都会对其进行测试。
它以与字符串开头匹配的A
锚点开头,因此这是该组可以成功的唯一位置。字符串开始后,在其他位置A
失败,并且由于该组是可选的,因此忽略其中的其余子模式,研究继续进行.*?hello
。
紧接着,有回溯控制动词(*COMMIT)
,如果之后失败,则强制根本不重试模式。(故事完)。
换句话说,如果这个小组在字符串开始时失败,研究就会一劳永逸地中止。
优点:它比基于G
的模式需要更少的步骤。
为了提高效率,也可以这样编写基于G
的模式(使用可选组而不是交替):
~(?:A.*?world)?(?!A).*?hello~sA
在这里,A 修饰符扮演G
锚点的角色,但它与使用G
锚点启动模式的每个分支(此处只有一个)完全相同。
一种方法可能是首先剥离字符串的前导部分,直到并包括第一次出现world
。 然后像您已经做的那样调用preg_match_all
并获取hello
的发生次数。
$input = "blah blah blah
hello
blah blah blah
class="world"
blah blah blah
hello
blah blah
hello
blah blah blah
hello
blah blah blah";
$input = preg_replace("/^.*?bworld/", "", $input);
preg_match_all("/bhellob/", $input, $matches);
echo sizeof($matches[0]); // 4