在这样的字符串中:
<body>
<img src="specialpic" />
<p>sometext</p><br>
<img src="/somepic.png" />
<img src="/somepic.png" />
<p>someotherstuff</p>
<img src="/anotherpic.png" />
<img src="/anotherpic.png" />
</body>
如何删除每个重复的img
标签,以便最终字符串如下所示?
<body>
<img src="specialpic" />
<p>sometext</p><br>
<img src="/somepic.png" />
<p>someotherstuff</p>
<img src="/anotherpic.png" />
</body>
为此
使用HTML解析器。请参阅此示例与 DOMDocument
。
初始化DOMDocument
并加载您的 HTML 文件:
$dom = new DOMDocument();
libxml_use_internal_errors(1);
$dom->formatOutput = True;
$dom->loadHTML($html);
初始化两个空数组:$img
将包含唯一的src
值,$toDelete
将包含要删除的重复节点:
$img = $toDelete = array();
搜索带有<img>
标签的所有节点:
$nodes = $dom->getElementsByTagName( 'img' );
对于每个找到的节点,将src
属性与数组进行比较$img
:如果找到,则将当前节点添加到$toDelete
,否则src
值添加到$img
:
foreach( $nodes as $node )
{
$src = $node->getAttribute('src' );
if( in_array( $src, $img ) ) $toDelete[] = $node;
else $img[] = $src;
}
最后,执行foreach
循环以删除找到的节点:
foreach( $toDelete as $node ) $node->parentNode->removeChild( $node );
要打印生成的 HTML,请执行以下操作:
echo $dom->saveHTML();
请注意$toDelete
数组的用法。理论上我们可以直接删除第一个foreach
内的节点,但通过这种方式我们减少了原始搜索结果的 len,因此跳过了下一个节点。
- 阅读更多 关于 DOMDocument
- 阅读为什么你不能用正则表达式解析 [X]HTML
虽然由于HTML的性质不完全推荐,但根据您的问题,并且假设图像标签始终采用相同的格式,或者在比较时完全相同的字符,这可以通过子模式实现。
试试这个:
$input =<<<EOF
<body>
<img src="specialpic" />
<p>sometext</p><br>
<img src="/somepic.png" />
<img src="/somepic.png" />
<p>someotherstuff</p>
<img src="/anotherpic.png" />
<img src="/anotherpic.png" />
</body>
EOF;
$result = preg_replace('|(<img's*src=.*?'s*/>'s*)'1*|s', ''1', $input);
结果应该是您所希望的确切输出。在这里查看: https://3v4l.org/sbgDX
-
's*
是匹配任何空格 -
.*?
是对介于两者之间的任何字符的非贪婪匹配 - 模式参数中的
'1*
表示子模式重复 0 或更多,在换行符后或匹配模式后的任何空格
这篇文章的灵感。