使用preg_match_all PHP限制结果数量

时间:2010-12-17 11:02:09

标签: php regex preg-match-all

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

例如,我想仅匹配网页上的前20个<p>代码,但有100个<p>代码。

干杯

8 个答案:

答案 0 :(得分:4)

不,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;
}

答案 1 :(得分:3)

$matches = array();   
preg_match_all ( $pattern , $subject , $matches );
$twenty = array_slice($matches , 0, 20);

答案 2 :(得分:3)

只需匹配all并对结果数组进行切片:

$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

答案 3 :(得分: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);

答案 4 :(得分:1)

要扩展@Gumbo的绝妙建议,即使用DOM解析器而不是regex,以下代码段将使用带有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'
---

答案 5 :(得分:0)

我不这么认为,但是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];
    }
}

答案 6 :(得分:0)

您可以使用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循环更清晰;)

答案 7 :(得分:0)

您可以使用T-Regx库:

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