用正则表达式删除所有空的HTML标签

5
这是我编写的PHP函数,用于从字符串输入中删除所有空的HTML标签:
/**
 * Remove the nested HTML empty tags from the string.
 *
 * @param $string String to remove tags
 * @param null $replaceTo Replace empty string with
 * @return mixed Cleaned string
 */
function crl_remove_empty_tags($string, $replaceTo = null)
{
    // Return if string not given or empty
    if (!is_string($string) || trim($string) == '') return $string;

    // Recursive empty HTML tags
    return preg_replace(
        '/<(\w+)\b(?:\s+[\w\-.:]+(?:\s*=\s*(?:"[^"]*"|"[^"]*"|[\w\-.:]+))?)*\s*/?>\s*</\1\s*>/gixsm',
        !is_string($replaceTo) ? '' : $replaceTo,
        $string
    );
}

我的正则表达式:/<(\w+)\b(?:\s+[\w\-.:]+(?:\s*=\s*(?:"[^"]*"|"[^"]*"|[\w\-.:]+))?)*\s*/?>\s*</\1\s*>/gixsm

我用http://gskinner.com/RegExr/http://regexpal.com/测试过,它的效果很好。 但是当我尝试运行它时,服务器总是返回错误:

Warning: preg_replace(): Unknown modifier '\'

我不知道 '\' 到底出了什么问题。有人可以帮助我吗?

可能是如何仅从字符串中删除HTML标记?的重复问题。 - Kumar V
很抱歉,但我想删除没有任何内容的HTML标签。这不是 strip_tags 的功能。 - Manhhailua
7个回答

10

在 PHP 正则表达式中,如果你的分隔符在表达式中以字面形式出现,你需要对它们进行转义。

在你的情况下,你有两个未转义的 /;只需用 \/ 替换它们即可。你也不需要修饰符数组——PHP 默认为全局匹配,并且你没有定义任何字面单词字符。

修改后:

/<(\w+)\b(?:\s+[\w\-.:]+(?:\s*=\s*(?:"[^"]*"|"[^"]*"|[\w\-.:]+))?)*\s*/?>\s*</\1\s*>/gixsm

之后:

/<(\w+)\b(?:\s+[\w\-.:]+(?:\s*=\s*(?:"[^"]*"|"[^"]*"|[\w\-.:]+))?)*\s*\/?>\s*<\/\1\s*>/
//                                                                    ^       ^

2
另外,移除 g 修饰符。PCRE 默认是全局的。 - Jonathan Kuhn
2
您可以删除所有修饰符。 - Casimir et Hippolyte
1
@CasimiretHippolyte确实,因为没有定义字面字母。 - brandonscript
谢谢大家!这是我的错。我对正则表达式很陌生,很难入手。 - Manhhailua
@MạnhHaiLúa很高兴看到问题得到了一些努力的解决;) - brandonscript
我需要排除仅为标签类型<a>的内容。 - Lenin Zapata

4

此模式能够删除“空标签”(即不是自闭合标签且不包含任何内容、空格、HTML注释或其他“空标签”的标签),即使这些标签像<span><span></span></span>那样嵌套。HTML注释中的标签不被考虑在内:

$pattern = <<<'EOD'
~
<
(?:
    !--[^-]*(?:-(?!->)[^-]*)*-->[^<]*(*SKIP)(*F) # skip comments
  |
    ( # group 1
        (\w++)     # tag name in group 2
        [^"'>]* #'"# all that is not a quote or a closing angle bracket
        (?: # quoted attributes
            "[^\\"]*(?:\\.[^\\"]*)*+" [^"'>]* #'"# double quote
          |
            '[^\\']*(?:\\.[^\\']*)*+' [^"'>]* #'"# single quote
        )*+
        >
        \s*
        (?:
            <!--[^-]*(?:-(?!->)[^-]*)*+--> \s* # html comments
          |
            <(?1) \s*                          # recursion with the group 1
        )*+
        </\2> # closing tag
    ) # end of the group 1
)
~sxi
EOD;

$html = preg_replace($pattern, '', $html);

限制:

  • 这种方法将删除外部JavaScript文件的链接:
    <script src="myscript.js"></script>
  • 模式可能会删除嵌入的Javascript代码的一部分,例如:
    var myvar="<span></span>";
    或者像这样:
    var myvar1="<span><!--"; function doSomething() { alert("!!!"); } var myvar2="--></span>";
    如果找到了这些内容,将被删除。

这些限制是由于基本文本方法无法区分HTML和Javascript代码。然而,如果您在模式跳过列表(与HTML注释相同的方式)中添加“script”标记,则可以解决此问题,但在这种情况下,您需要基本描述不是简单任务但是可能的Javascript内容(字符串,注释,文字模式,所有不是前三个的东西)。


这比我的好多了!非常棒且有帮助!谢谢! - Manhhailua
有没有办法让它在PHP 5.3(PCRE 8.12 2011-01-15)上运行? 它似乎与任何东西都不匹配,而在正则表达式测试网站(phpliveregex或regex101)上的情况很好。 - MarkL
@MarkL:没有理由说这个模式在PHP 5.3及其对应的PCRE版本上不起作用,你可能漏掉了什么东西。我建议在代码开头添加ini_set('display_errors', 'On');来查看发生了什么。无论如何,我会尽快改进这个答案。 - Casimir et Hippolyte
无法让它正常运作。尝试了三个不同的服务器和各种版本的PHP/PCRE,错误显示已经启用。该数组生成空值。但是phpliveregex.com可以正常工作。我都要抓狂了。示例代码:http://pastie.org/10310797 如果我逐字使用您的代码(包括EOD等),它就能正常工作。发生了什么? - MarkL
@MarkL:你的问题与如何根据引号确定字符串中的反斜杠字面量有关。请参阅https://eval.in/405609以及PHP手册中有关字符串和八进制数的内容。 - Casimir et Hippolyte

3
删除空元素...以及下一个空元素。
例如:
<p>Hello!
   <div class="foo"><p id="nobody">
   </p>
      </div>
 </p>

结果:

<p>Hello!</p>

PHP 代码:

/* $html store the html content */
do {
    $tmp = $html;
    $html = preg_replace( '#<([^ >]+)[^>]*>([[:space:]]|&nbsp;)*</\1>#', '', $html );
} while ( $html !== $tmp );

确实非常不错!如果想要进行终极杀手级别的空格演化,请尝试使用以下代码:'#<([^ >]+)[^>]*>([[:space:]]|&nbsp;)*</\1>#' - Gruber

0

这里有另一种删除所有空标签的方法。(如果它们由于空子元素而被视为空,则还会删除周围的标签:

/**
 * Remove empty tags.
 * This one will also remove <p><a href="/foo/bar.baz"><span></span></a></p> (empty paragraph with empty link)
 * But it will not alter <p><a href="/foo/bar.baz"><span>[CONTENT HERE]</span></a></p> (since the span has content)
 *
 * Be aware: <img ../> will be treated as an empty tag!
 */
do
{
    $len1 = mb_strlen($string);
    $string = preg_replace('/<(\w+)\b(?:\s+[\w\-.:]+(?:\s*=\s*(?:"[^"]*"|"[^"]*"|[\w\-.:]+))?)*\s*\/?>\s*<\/\1\s*>/', '', $string);
    $len2 = mb_strlen($string);

} while ($len1 > 0 && $len2 > 0 && $len1 != $len2);

我一直在使用这个工具来从外部CMS中清理HTML,并取得了积极的效果。


0

不太确定这是否是您需要的,但我今天找到了这个。您需要 PHP 5.4+!

$oDOMHTML = DOMDocument::loadHTML( 
    $sYourHTMLString, 
    LIBXML_HTML_NOIMPLIED | 
    LIBXML_HTML_NODEFDTD | 
    LIBXML_NOBLANKS | 
    LIBXML_NOEMPTYTAG 
);
$sYourHTMLStringWithoutEmptyTags = $oDOMHTML->saveXML();

也许这对你有用。


我已经尝试过了,但是没有成功。我的PHP版本是5.4.19。我会在以后的时间再试一次(等我理解了)。谢谢! - Manhhailua
也许你可以给我一些示例代码,这段代码是没有经过任何测试的。今天下午刚刚阅读了LIBXML_*常量。 - boesing
嗯,显然这些选项只是将“<br></br>”转换为“<br/>”。同样的,它也会将“<span></span>”转换为“<span/>”。抱歉,我以为这可能有所帮助。不过还是值得一试的。 - boesing

0

你也可以使用递归来解决这个问题。继续将 HTML 代码块传回函数,直到空标签不再存在。

public static function removeHTMLTagsWithNoContent($htmlBlob) {
    $pattern = "/<[^\/>][^>]*><\/[^>]+>/";

    if (preg_match($pattern, $htmlBlob) == 1) {
        $htmlBlob = preg_replace($pattern, '', $htmlBlob);
        return self::removeHTMLTagsWithNoContent($htmlBlob);
    } else {
        return $htmlBlob;
    }
}

这将检查空HTML标签的存在并替换它们,直到正则表达式模式不再匹配。


0
$string = '<p>Some <b>HTML</b> <strong>text. </strong> <hr></p>';
$clean_string = preg_replace('#<[^>]+>#', '', $string);
echo $clean_string; // Some HTML text. 

1
你的回答包含没有解释的代码。最好包括关于如何回答问题的详细信息。 - Kevin M. Mansour

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接