提问人:Jeff 提问时间:11/13/2009 更新时间:10/1/2011 访问量:2605
如何使用PHP平衡标签
How to balance tags with PHP
问:
在下面的字符串中,我想用一些文本替换,然后截断字符串。<!--more-->
FOOBAR
<p>The quick <a href="/">brown</a> fox jumps <!--more-->
over the <a href="/">lazy</a> dog.</p>
我已经到了这个地步:
<p>The quick <a href="/">brown</a> fox jumps FOOBAR
...但正如你所看到的,标签没有关闭。关于如何始终如一地平衡标签的任何想法?我对PHP很陌生。<p>
我正在使用的数组如下所示:
array(2) {
[0]=>
string(50) "<p>The quick <a href="/">brown</a> fox jumps "
[1]=>
string(45) " over the <a href="/">lazy</a> dog.</p>"
}
答:
0赞
Seb
11/13/2009
#1
当你陈述问题时,就这么简单:
str_replace('<!--more-->', 'FOOBAR', $original_text);
也许如果你更新你的问题来解释数组与整个问题有什么关系,将有助于解释正确的问题 - (字符串应该在数组中吗?<!--more-->
评论
0赞
Tim Lytle
11/13/2009
他想替换文本,然后在替换后删除(截断)所有内容,但仍保留任何结束标记。
0赞
Jeff
11/13/2009
是的,我在发布后一分钟添加了那个截断位。不好意思。我有一种感觉,这是一个“加载”的问题,它比我最初想象的要复杂,因为所有可能的标签组合。
2赞
qid
11/13/2009
#2
如果可能的话,我建议将 HTML 解析为 DOM 并以这种方式处理它,遍历文本节点直到找到该字符串,然后截断文本节点并删除该子节点之后的任何其他子节点(保持父节点不变)。然后将 DOM 重新序列化为 HTML。
评论
1赞
Jeff
11/13/2009
好吧,正如我所说,我对PHP很陌生。如何将 HTML 解析为 DOM(哪个 php 函数)?然后我就很好了,直到“将 DOM 重新序列化为 HTML”。不。
1赞
Jeff
11/13/2009
我真的很想了解更多关于这个评论的信息。我在这个页面上走在正确的轨道上吗: php.net/manual/en/book.dom.php ?
0赞
gnud
11/13/2009
#3
您必须在占位符文本之前找到所有已打开但未关闭的标记。 像现在一样插入新文本,然后关闭标签。
这是一个草率的例子。我认为这段代码适用于所有有效的 HTML,但我并不肯定。它肯定会接受无效的标记。但无论如何:
$h = '<p>The quick <a href="/">brown</a> fox jumps <!--more-->
over the <a href="/">lazy</a> dog.</p>';
$parts = explode("<!--more-->", $h, 2);
$front = $parts[0];
/* Find all opened tags in the front string */
$tags = array();
preg_match_all("|<([a-z][\w]*)(?: +\w*=\"[\\w/%&=]+\")*>|i", $front, $tags, PREG_OFFSET_CAPTURE);
array_shift($tags); /* get rid of the complete match from preg_match_all */
/* Check if the opened arrays have been closed in the front string */
$unclosed = array();
foreach($tags as $t) {
list($tag, $pos) = $t[0];
if(strpos($front, "</".$tag, $pos) == false) {
$unclosed[] = $tag;
}
}
/* Print the start, the replacement, and then close any open tags. */
echo $front;
echo "FOOBAR";
foreach($unclosed as $tag) {
echo "</".$tag.">";
}
输出
<p>The quick <a href="/">brown</a> fox jumps FOOBAR</p>
1赞
mcrumley
11/13/2009
#4
我还没有完全测试过这一点,但它至少适用于您的示例。假定 XML 格式正确。
<?php
$reader = new XMLReader;
$writer = new XMLWriter;
// load the XML string into the XMLReader
$reader->xml('<p>The quick <a href="/">brown</a> fox jumps <!--more--> over the <a href="/">lazy</a> dog.</p>');
// write the new XML to memory
$writer->openMemory();
$done = false;
// XMLReader::read() moves the current read location to the next node
while ( !$done && $reader->read()) {
// choose action based on the node type
switch ($reader->nodeType) {
case XMLReader::ELEMENT:
// read an element, so write it back to the output
$writer->startElement($reader->name);
if ($reader->hasAttributes) {
// loop through all attributes and write them
while($reader->moveToNextAttribute()) {
$writer->writeAttribute($reader->name, $reader->value);
}
// move back to the beginning of the element
$reader->moveToElement();
}
// if the tag is empty, close it now
if ($reader->isEmptyElement) {
$writer->endElement();
}
break;
case XMLReader::END_ELEMENT:
$writer->endElement();
break;
case XMLReader::TEXT:
$writer->text($reader->value);
break;
case XMLReader::COMMENT:
// you can change this to be more flexible if you need
// e.g. preg_match, trim, etc.
if (trim($reader->value) == 'more') {
// write whatever you want in here. If you have xml text
// you want to write verbatim, use writeRaw() instead of text()
$writer->text('FOOBAR');
// this is where the magic happens -- endDocument closes
// any remaining open tags
$writer->endDocument();
// stop the loop (could use "break 2", but that gets confusing
$done = true;
}
break;
}
}
echo $writer->outputMemory();
评论
0赞
Jeff
11/13/2009
还没有尝试过,但一会儿就会尝试。您上面的代码与 php.net/manual/en/book.dom.php 相似吗?我还没有上过课,但我已经盯着那一页看了一段时间,试图弄清楚。
0赞
mcrumley
11/13/2009
您可以使用 DOM 对象完成相同的操作,但我发现 XMLReader/XMLWriter 更容易用于这样的简单过滤器。DOMDocument 适用于更复杂的操作,例如移动整个节点树。
0赞
Jeff
11/13/2009
谢谢麦克拉姆利。想知道你是否介意在一些代码中添加注释以帮助我理解它。事实上,我可以看到我需要对“更多”值进行修改,因为我的值实际上比这要复杂一些。
0赞
Jeff
11/13/2009
很棒的评论,谢谢!与这样的脚本相比,这将产生什么样的服务器开销:snipplr.com/view/3618/close-tags-in-a-htmlsnippet?我之所以问,是因为这个函数将在每次页面加载时调用,通常每次调用十几次。
0赞
mcrumley
11/17/2009
对不起,回复晚了,我周末不在城里。这两种算法的时序主要取决于输入的复杂程度以及中断发生的位置。在我运行的测试中,我认为典型输入的性能大致相同。您的里程可能会有所不同。无论如何,跑步 100 次的时间都不超过 0.03 秒。
4赞
rjha94
10/1/2011
#5
您可以使用wordpress force_balance_tags功能。实现就在这里:-
http://core.trac.wordpress.org/browser/trunk/wp-includes/formatting.php
这是一个独立的函数,您只需在代码中复制+粘贴即可。
function force_balance_tags( $text ) {
用法简单
$bad_text = "<div> <p> some text </p> " ;
回声 force_balance_tags($bad_text);
由于这是 WordPress 的一部分,因此它经过了尝试和测试,并且比 adHoc regex macthing 解决方案更好。
评论
0赞
stevevls
8/23/2012
+1 多么方便...碰巧的是,我希望在我的 WP 插件中平衡标签。
评论