PHP中删除所有空HTML标签对

3
我正在寻找一种方法来删除所有空的HTML标签对,例如<strong></strong><p class="bold"></p>。虽然可以相对容易地找到用于此目的的正则表达式,但我找不到一个可靠地与PHP的preg_replace()配合使用的正则表达式。以下是我尝试过的函数之一(摘自https://stackoverflow.com/a/5573115/1784564):
function strip_empty_tags($text) {
    // Match empty elements (attribute values may have angle brackets).
    $re = '%
        # Regex to match an empty HTML 4.01 Transitional element.
        <                    # Opening tag opening "<" delimiter.
        ((?!iframe)\w+)\b    # $1 Tag name.
        (?:                  # Non-capture group for optional attribute(s).
          \s+                # Attributes must be separated by whitespace.
          [\w\-.:]+          # Attribute name is required for attr=value pair.
          (?:                # Non-capture group for optional attribute value.
            \s*=\s*          # Name and value separated by "=" and optional ws.
            (?:              # Non-capture group for attrib value alternatives.
              "[^"]*"        # Double quoted string.
            | \'[^\']*\'     # Single quoted string.
            | [\w\-.:]+      # Non-quoted attrib value can be A-Z0-9-._:
            )                # End of attribute value alternatives.
          )?                 # Attribute value is optional.
        )*                   # Allow zero or more attribute=value pairs
        \s*                  # Whitespace is allowed before closing delimiter.
        >                    # Opening tag closing ">" delimiter.
        \s*                  # Content is zero or more whitespace.
        </\1\s*>             # Element closing tag.
        %x';
    while (preg_match($re, $text)) {
        // Recursively remove innermost empty elements.
        $text = preg_replace($re, '', $text);
    }

    return $text;
}

这是我一直在测试的HTML:

<strong class="a b">Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.l<br class="a  b" />fd<br class="a  b" /><br class="a  b" /></strong><strong class="a b"></strong><strong class="a b"><br class="a  b" /></strong><strong class="a b"></strong><br class="a  b" /><strong class="a b"><br class="a  b" /><br class="a  b" /></strong>

到目前为止,我尝试了所有方法(已经花费4个多小时的时间),似乎都能去除一些标签,但并非全部,这让我感到疯狂。如有帮助,将不胜感激。


那个特定的正则表达式并没有寻找自闭合标签。你可能希望添加一个替代 >\s*</\1> 的正则表达式。(如果你不熟悉正则表达式,可以考虑研究XPath。) - mario
只有两个空标签,对吧? - vks
1
你的问题中HTML示例文本标记没有空元素需要被剥离(除了自闭合的<br />标签)。每个看起来为空的<STRONG>标签实际上包含两个“``”非空格Unicode字符 - (\uFEFF)。在我看来,这似乎是一个网页字符编码问题。 - ridgerunner
@ridgerunner,哇。你是怎么检测出来的? - Artem Gordinsky
@Artem Gordinsky - 复制并粘贴到文本编辑器中。(请注意,作为上述正则表达式/函数的作者,我非常乐意回答任何人可能遇到的问题。) - ridgerunner
2个回答

6
需要一个 unicode正则表达式,因为示例中的“空”标记实际上是非空的:
$re = '~<(\w+)[^>]*>[\p{Z}\p{C}]*</\1>~u';

\p{Z} ... 包括任何类型的空格或不可见分隔符
\p{C} ... 不可见控制字符和未使用的代码点

使用 u (PCRE_UTF8) 修饰符; 在regex101上测试


为了包括空元素<br><br />

$re = '~<(\w+)[^>]*>(?>[\p{Z}\p{C}]|<br\b[^>]*>)*</\1>~ui';

在正则表达式 101 上的测试


还要匹配带有空格实体的标签

$re = '~<(\w+)[^>]*>(?>[\p{Z}\p{C}]|<br\b[^>]*>|&(?:(?:nb|thin|zwnb|e[nm])sp|zwnj|#xfeff|#xa0|#160|#65279);)*</\1>~iu'

在regex101上测试;根据您的需要进行修改。


使用递归正则表达式(不使用while循环)

$re = '~<(\w+)[^>]*>(?>[\p{Z}\p{C}]|<br\b[^>]*>|&(?:(?:nb|thin|zwnb|e[nm])sp|zwnj|#xfeff|#xa0|#160|#65279);|(?R))*</\1>~iu';

在regex101上测试


1
那些正则表达式真是让人难以理解,但它们的效果非常好!有没有办法允许特定的空标签,比如通常为空的“iframe”或“canvas”? - Mark Notton
@MarkNotton 当然,你可以在 < 后面添加一个负向前瞻,以任何你喜欢的版本为准:<(?!(?:iframe|canvas)\b)... - Jonny 5

1

在Jonny 5的回答中,我对其发表了评论;由于iframecanvas通常可以为空,因此我将一些可接受的标签添加到递归正则表达式中。

$re = '~<((?!iframe|canvas)\w+)[^>]*>(?>[\p{Z}\p{C}]|<br\b[^>]*>|&(?:(?:nb|thin|zwnb|e[nm])sp|zwnj|#xfeff|#xa0|#160|#65279);|(?R))*</\1>~iu';

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