【问题标题】:Limit the number of results using preg_match_all PHP使用 preg_match_all PHP 限制结果数
【发布时间】:2011-05-27 01:43:22
【问题描述】:

有没有办法限制使用preg_match_all 返回的匹配数?

例如,我只想匹配网页上的前 20 个 <p> 标签,但有 100 个 <p> 标签。

干杯

【问题讨论】:

    标签: php regex preg-match-all


    【解决方案1】:

    这是真正的答案;最节省内存的方式。
    改为通过preg_replace_callback() 使用引用分配

    <?php
    
    $matches = [];
    
    preg_replace_callback(
        '~<p(?:\s.*?)?>(?:.*?)</p>~s',
        function (array $match) use (&$matches) {
            $matches[] = $match[0];
        },
        $html,
        20,
        $_
    );
    
    var_dump($matches);
    

    【讨论】:

    • 当 p 标签有属性或者它们的 innerhtml 跨越超过一行时不起作用。兔子洞开始了。请在解析 html 时建议不要使用正则表达式。
    • 另外,据我统计,preg_replace_callback() 最多有 5 个参数。 ($pattern, $callback, $subject, $limit, and $count) PREG_SET_ORDER 不是必需的,也不会被兑现。我也不认为声明 count 变量对这种情况有价值。 php.net/manual/en/function.preg-replace-callback.php
    • @mickmackusa 我的 sn-p 不适合这个问题,抱歉。那是为了我的工作。
    • @mickmackusa 这是文档错误。 php-src 说它有 6 个参数。 github.com/php/php-src/blob/…
    • 酷。谢谢你让我知道。尽管如此,最后两个参数并不是真正需要的。
    【解决方案2】:

    为了延续@Gumbo 的建议,使用DOM 解析器而不是正则表达式,下面的sn-p 将使用带有position() 条件的XPath 查询来限制目标标签。

    代码:(Demo targeting 4 of 5 p tags)

    $html = <<<HTML
    <div>
        <p class="classy">1
    </p>
        <p>2</p>
        <p data-p="<p>notatag</p>">3</p>
        <span data-monkeywrench='<p'>z</span>
        <p
     data-p="<p>notatag</p>">4</p>
        <p>5</p>
    </div>
    HTML;
    
    $dom = new DOMDocument();
    $dom->loadHTML($html, LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD);
    $xpath = new DOMXPath($dom);
    foreach ($xpath->query('//p[position() <= 4]') as $p) {
        echo var_export($p->nodeValue, true) , "\n---\n";
    }
    

    输出:

    '1
    '
    ---
    '2'
    ---
    '3'
    ---
    '4'
    ---
    

    【讨论】:

      【解决方案3】:

      你可以使用T-Regx库:

      pattern('<p>')->match($yourHtml)->only(20);
      

      【讨论】:

        【解决方案4】:

        我不这么认为,但preg_match 确实有一个offset 参数,还有一个PREG_OFFSET_CAPTURE 标志,当它们组合时,可用于获得“下一场比赛”。

        如果您不想获得所有结果然后array_slice() 部分关闭,这主要是有用的:o)

        编辑: 好的,这是一些代码(未经测试或以任何方式使用):

        $offset = 0;
        $matches = array();
        for ($i = 0; $i < 20; $i++) {
            $results = preg_match('/<p(?:.*?)>/', $string, PREG_OFFSET_CAPTURE, $offset);
            if (empty($results)) {
                break;
            } else {
                $matches[] = $results[0][0];
                $offset += $results[0][1];
            }
        }
        

        【讨论】:

          【解决方案5】:

          您可以使用preg_match_all() 并丢弃您不感兴趣的匹配项,也可以使用带有preg_match() 的循环。如果您担心扫描大字符串的费用,第二个选项会更好。

          此示例限制为 2 个匹配项,而整个字符串中实际上有 3 个:

          <?php
          
          $str = "ab1ab2ab3ab4c";
          
          for ($offset = 0, $n = 0;
                  $n < 2 && preg_match('/b([0-9])/', $str, $matches, PREG_OFFSET_CAPTURE, $offset);
                  ++$n, $offset = $matches[0][1] + 1) {
          
                  var_dump($matches);
          }
          

          实际上,while 循环可能比 for 循环反射更清晰;)

          【讨论】:

            【解决方案6】:

            不,preg_match_all 结果集的计算不能被限制。之后您只能使用array_slicearray_splice 限制结果(这需要PREG_SET_ORDER):

            preg_match_all($pattern, $subject, $matches, PREG_SET_ORDER);
            $firstMatches = array_slice($matches, 0, 20);
            

            但除此之外,你不应该使用正则表达式来解析 HTML。尽管现代正则表达式引擎不再是正则表达式,并且可以处理 HTML 等不规则语言,但它太容易出错。最好使用适当的 HTML 解析器,而不是像 PHP’s DOM library 那样的解析器。然后只需使用计数器最多只能获得 20 个匹配项:

            $doc = new DOMDocument();
            $doc->loadHTML($code);
            $counter = 20;
            $matches = array();
            foreach ($doc->getElementsByTagName('p') as $elem) {
                if ($counter-- <= 0) {
                    break;
                }
                $matches[] = $elem;
            }
            

            【讨论】:

            • 干杯 Gumbo,这个 DOM 东西真的很有用。从来没有尝试过在 HTML 上使用它而不是 reg ex,所以试试吧!
            • @SiQ:请注意,DOMDocument 实现了 W3C 指定的 DOM,因此非常广泛;如果你只是需要阅读 DOM,你也可以试试SimpleXML
            • 您的 DOMDocument 解决方案也在事后限制了代码,不是吗?它没有设置限制,但会忽略它收集的额外标签。
            【解决方案7】:

            只匹配所有并切片结果数组:

            $allMatches = array ();
            $numMatches = preg_match_all($pattern, $subject, $allMatches, PREG_SET_ORDER);
            $limit = 20;
            $limitedResults = $allMatches;
            if($numMatches > $limit)
            {
               $limitedResults = array_slice($allMatches, 0, $limit);
            }
            
            // Use $limitedResults here
            

            【讨论】:

              【解决方案8】:
              $matches = array();   
              preg_match_all ( $pattern , $subject , $matches );
              $twenty = array_slice($matches , 0, 20);
              

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 2015-10-17
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2022-01-25
                • 1970-01-01
                • 2016-04-25
                • 2013-04-16
                相关资源
                最近更新 更多