在找到不同的特定词后计算特定词的出现次数

Count occurrences of specific word after a different, specific word is found

我对正则表达式还比较陌生,并且一直停留在以下我尝试使用 preg_match_allcount the number of hello after world 的地方。

如果我用"world".+(hello),算到最后hello"world".*?(hello) 停在第一个 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 不应该被计数。

一种方法可能是首先去除字符串的前导部分,直到并包括第一次出现的 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("/\bhello\b/", $input, $matches);
echo sizeof($matches[0]);  // 4

您可以在此处使用单个 preg_match_all 调用:

$text = "blah blah blah\nhello\nblah blah blah\nclass=\"world\" \nblah blah blah\nhello \nblah blah\nhello\nblah blah blah\nhello\nblah blah blah";
echo preg_match_all('~(?:\G(?!^)|\bworld\b).*?\K\bhello\b~s', $text);

regex demo and the PHP demo详情:

  • (?:\G(?!^)|\bworld\b) - 上一场比赛的结束(\G(?!^) 做这个检查:\G matches either start of the string or end of the previous match position,所以我们需要排除字符串位置的开始,这是通过 (?!^) 负前瞻)或整个单词 world
  • .*? - 任意零个或多个字符,尽可能少
  • \K - 丢弃目前匹配的所有文本
  • \bhello\b - 一个完整的单词 hello.

注意:如果不需要单词边界检查,可以从模式中删除\b

如果helloworld是用户定义的模式,你必须preg_quote他们在模式中:

$start = "world";
$find = "hello";
$text = "blah blah blah\nhello\nblah blah blah\nclass=\"world\" \nblah blah blah\nhello \nblah blah\nhello\nblah blah blah\nhello\nblah blah blah";
echo preg_match_all('~(?:\G(?!^)|' . preg_quote($start, '~') . '\b).*?\K' . preg_quote($find, '~') . '~s', $text);

另一个带有简单正则表达式的选项:

if(preg_match('/"world".*/s', $str, $out)) {
  echo preg_match_all('/\bhello\b/', $out[0]);
}

See demo at tio.run

其他方式:如果字符串中不存在 world,则强制模式失败并且不重试:

~(?:\A(*COMMIT).*?world)?.*?hello~s

demo

非捕获组是可选的但贪婪的。结果,每次尝试模式时都会对其进行测试。
它以匹配字符串开头的 \A 锚点开始,因此这是该组唯一可以成功的位置。在字符串开始之后,在其他位置 \A 失败并且由于该组是可选的,因此忽略其中剩余的子模式并继续研究 .*?hello.
紧随其后的是回溯控制动词 (*COMMIT) ,万一在它之后失败,则强制根本不重试该模式。 (故事结束)。

换句话说,如果该组在字符串的开头失败,则研究将永远中止。

优点:与基于 \G 的模式相比,它需要的步骤更少。


为了提高效率,基于 \G 的模式也可以这样写(使用可选组而不是交替):

~(?:\A.*?world)?(?!\A).*?hello~sA

这里的修饰符 A 起到了 \G 锚点的作用,但它与用 \G 锚点开始一个模式的每个分支(这里只有一个)完全一样。