关闭字符串中的打开HTML标记

Ahm*_*uad 15 php regex string

情境是一个字符串,导致如下所示:

<p>This is some text and here is a <strong>bold text then the post stop here....</p>
Run Code Online (Sandbox Code Playgroud)

因为该函数返回文本的预告片(摘要),所以它会在某些单词后停止.在这种情况下,标签强不关闭.但整个字符串都包含在一个段落中.

是否可以将上述结果/输出转换为以下内容:

<p>This is some text and here is a <strong>bold text then the post stop here....</strong></p>
Run Code Online (Sandbox Code Playgroud)

我不知道从哪里开始.问题是..我发现这确实是正则表达式在网络上的功能,但它的字符串后提出的结束标记.因此它不会验证,因为我想要的段落标记内的所有打开/关闭标签.我发现的功能也是错误的:

<p>This is some text and here is a <strong>bold text then the post stop here....</p></strong>
Run Code Online (Sandbox Code Playgroud)

我想知道标签可以是强大的,斜体的,任何东西.这就是为什么我无法附加函数并在函数中手动关闭它.任何可以为我做的模式?

ale*_*exn 34

Here is a function i've used before, which works pretty well:

function closetags($html) {
    preg_match_all('#<(?!meta|img|br|hr|input\b)\b([a-z]+)(?: .*)?(?<![/|/ ])>#iU', $html, $result);
    $openedtags = $result[1];
    preg_match_all('#</([a-z]+)>#iU', $html, $result);
    $closedtags = $result[1];
    $len_opened = count($openedtags);
    if (count($closedtags) == $len_opened) {
        return $html;
    }
    $openedtags = array_reverse($openedtags);
    for ($i=0; $i < $len_opened; $i++) {
        if (!in_array($openedtags[$i], $closedtags)) {
            $html .= '</'.$openedtags[$i].'>';
        } else {
            unset($closedtags[array_search($openedtags[$i], $closedtags)]);
        }
    }
    return $html;
} 
Run Code Online (Sandbox Code Playgroud)

Personally though, I would not do it using regexp but a library such as Tidy. This would be something like the following:

$str = '<p>This is some text and here is a <strong>bold text then the post stop here....</p>';
$tidy = new Tidy();
$clean = $tidy->repairString($str, array(
    'output-xml' => true,
    'input-xml' => true
));
echo $clean;
Run Code Online (Sandbox Code Playgroud)

  • 谢谢你把我介绍给Tidy.这是一个很多辉煌的事:) (2认同)

Mar*_*kus 9

对原始答案的一个小修改......而原始答案正确剥离了标签.我发现在我的截断期间,我最终可能会被切碎的标签.例如:

This text has some <b>in it</b>
Run Code Online (Sandbox Code Playgroud)

截断字符21会导致:

This text has some <
Run Code Online (Sandbox Code Playgroud)

以下代码构建于下一个最佳答案并修复此问题.

function truncateHTML($html, $length)
{
    $truncatedText = substr($html, $length);
    $pos = strpos($truncatedText, ">");
    if($pos !== false)
    {
        $html = substr($html, 0,$length + $pos + 1);
    }
    else
    {
        $html = substr($html, 0,$length);
    }

    preg_match_all('#<(?!meta|img|br|hr|input\b)\b([a-z]+)(?: .*)?(?<![/|/ ])>#iU', $html, $result);
    $openedtags = $result[1];

    preg_match_all('#</([a-z]+)>#iU', $html, $result);
    $closedtags = $result[1];

    $len_opened = count($openedtags);

    if (count($closedtags) == $len_opened)
    {
        return $html;
    }

    $openedtags = array_reverse($openedtags);
    for ($i=0; $i < $len_opened; $i++)
    {
        if (!in_array($openedtags[$i], $closedtags))
        {
            $html .= '</'.$openedtags[$i].'>';
        }
        else
        {
            unset($closedtags[array_search($openedtags[$i], $closedtags)]);
        }
    }


    return $html;
}


$str = "This text has <b>bold</b> in it</b>";
print "Test 1 - Truncate with no tag: " . truncateHTML($str, 5) . "<br>\n";
print "Test 2 - Truncate at start of tag: " . truncateHTML($str, 20) . "<br>\n";
print "Test 3 - Truncate in the middle of a tag: " . truncateHTML($str, 16) . "<br>\n";
print "Test 4: - Truncate with less text: " . truncateHTML($str, 300) . "<br>\n";
Run Code Online (Sandbox Code Playgroud)

希望它可以帮助那里的人.


小智 5

那么使用 PHP 的原生 DOMDocument 类怎么样?它本质上解析 HTML 并纠正语法错误...例如:

$fragment = "<article><h3>Title</h3><p>Unclosed";
$doc = new DOMDocument();
$doc->loadHTML($fragment);
$correctFragment = $doc->getElementsByTagName('body')->item(0)->C14N();
echo $correctFragment;
Run Code Online (Sandbox Code Playgroud)

然而,这种方法有几个缺点。首先,它将原始片段包装在<body>标签内。您可以通过 (preg_)replace() 之类的方法轻松摆脱它,或者用...->C14N()一些自定义的 innerHTML() 函数替换该函数,例如http://php.net/manual/en/book.dom 中建议的那样。 php#89718。第二个陷阱是,如果使用 HTML5 或自定义标签,PHP 会抛出“实体中的无效标签”警告(尽管如此,它仍然会正确执行)。