代码之家  ›  专栏  ›  技术社区  ›  Jeff

如何用PHP平衡标签

php
  •  2
  • Jeff  · 技术社区  · 16 年前

    在下面的字符串中,我要替换 <!--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
    

    …但正如你所见, <p> 标记未关闭。我对如何持续平衡标签有什么想法吗?我对PHP很陌生。

    我使用的数组如下:

    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>"
    }
    
    5 回复  |  直到 14 年前
        1
  •  1
  •   mcrumley    16 年前

    我还没有完全测试过这个,但它至少对您的示例有效。假定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();
    
        2
  •  4
  •   rjha94    14 年前

    您可以使用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> " ;
    

    echo force_balance_标签($bad_文本);

    因为这是WordPress的一部分,所以它经过了尝试和测试,并且优于Adhoc Regex Maching解决方案。

        3
  •  2
  •   qid    16 年前

    如果可能的话,我建议将HTML解析成一个DOM并用这种方式处理它,遍历文本节点直到找到该字符串,然后截断文本节点并进一步删除。 小孩 之后的节点(保持父节点不变)。然后将DOM重新序列化为HTML。

        4
  •  0
  •   Seb    16 年前

    当你陈述问题的时候,就这么简单:

    str_replace('<!--more-->', 'FOOBAR', $original_text);
    

    也许,如果您更新您的问题来解释数组与整个问题的关系,将有助于解释正确的问题——(字符串 <!--more--> 应该在阵列中吗?)

        5
  •  0
  •   gnud    16 年前

    在占位符文本之前,必须找到所有已打开但未关闭的标记。 像现在一样插入新文本,然后关闭标签。

    这里有一个草率的例子。我认为这段代码适用于所有有效的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>
    
    推荐文章