簡體   English   中英

使用 preg_match_all PHP 限制結果數量

[英]Limit the number of results using preg_match_all PHP

有什么方法可以限制使用preg_match_all返回的匹配項數量?

例如,我只想匹配網頁上的前 20 個<p>標簽,但有 100 個<p>標簽。

干杯

不可以,不能限制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 的 DOM 庫之一 然后只需使用計數器最多只能獲得 20 個匹配項:

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

只需匹配所有並切片結果數組:

$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

您可以使用T-Regx庫:

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

這才是真正的答案; 最節省內存的方式。
改為通過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);

為了擴展 @Gumbo 使用 DOM 解析器而不是正則表達式的偉大建議,以下代碼段將使用帶有position()條件的 XPath 查詢來限制目標標簽。

代碼:(針對 5 個標簽中的 4 個的演示

$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'
---

我不這么認為,但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];
    }
}

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

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM