【问题标题】:Count occurrences of specific word after a different, specific word is found在找到不同的特定单词后计算特定单词的出现次数
【发布时间】:2021-12-18 13:13:45
【问题描述】:

我对正则表达式相当陌生,并且在尝试使用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 计数。

【问题讨论】:

标签: php regex preg-match-all counting


【解决方案1】:

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

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

See demo at tio.run

【讨论】:

  • 这不是和@Tim Biegeleisen 建议的一样吗?抓住 world 之后的任何东西,然后数 hello 整个单词?
  • @deanstreet 这个 1.) 匹配 "world"... 以及它之后的任何内容 2.) 如果有匹配,它会在输出中计算所有 hellos。字符串中必须存在"world",并且hellos 出现在"world" 之后才能计入此字符串。
  • 作为一个新手,这种分而治之的方法肯定比上面建议的单行正则表达式更容易消化,但单行工作得很好。
【解决方案2】:

您可以在此处使用单个 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 demoPHP demo详情

  • (?:\G(?!^)|\bworld\b) - 上一场比赛的结束(\G(?!^) 会检查:\G matches either start of the string or end of the previous match position,所以我们需要排除字符串位置的开始,这是通过 (?!^) 否定前瞻来完成的)或整个单词 @987654333 @
  • .*? - 任何零个或多个字符,尽可能少
  • \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);

【讨论】:

【解决方案3】:

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

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

demo

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

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

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


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

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

这里的 A 修饰符扮演 \G 锚点的角色,但它与以 \G 锚点开始模式的每个分支(这里只有一个)完全相同。

【讨论】:

    【解决方案4】:

    一种方法可能是首先去掉字符串的前导部分,直到 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
    

    【讨论】:

    • 这个答案是有缺陷的,因为如果字符串中不存在触发词 (world),它将失败。即使缺少world,输出仍然是4。证明:3v4l.org/58vXC
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2022-01-04
    • 2020-09-09
    • 1970-01-01
    • 1970-01-01
    • 2014-08-24
    • 2015-06-09
    • 1970-01-01
    相关资源
    最近更新 更多