【发布时间】:2011-05-27 01:43:22
【问题描述】:
有没有办法限制使用preg_match_all 返回的匹配数?
例如,我只想匹配网页上的前 20 个 <p> 标签,但有 100 个 <p> 标签。
干杯
【问题讨论】:
标签: php regex preg-match-all
有没有办法限制使用preg_match_all 返回的匹配数?
例如,我只想匹配网页上的前 20 个 <p> 标签,但有 100 个 <p> 标签。
干杯
【问题讨论】:
标签: php regex preg-match-all
这是真正的答案;最节省内存的方式。
改为通过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);
【讨论】:
preg_replace_callback() 最多有 5 个参数。 ($pattern, $callback, $subject, $limit, and $count) PREG_SET_ORDER 不是必需的,也不会被兑现。我也不认为声明 count 变量对这种情况有价值。 php.net/manual/en/function.preg-replace-callback.php
为了延续@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'
---
【讨论】:
你可以使用T-Regx库:
pattern('<p>')->match($yourHtml)->only(20);
【讨论】:
我不这么认为,但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 循环反射更清晰;)
【讨论】:
不,preg_match_all 结果集的计算不能被限制。之后您只能使用array_slice 或array_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;
}
【讨论】:
只匹配所有并切片结果数组:
$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
【讨论】:
$matches = array();
preg_match_all ( $pattern , $subject , $matches );
$twenty = array_slice($matches , 0, 20);
【讨论】: