检测字符串中的 HTML 并用代码标记包装


detect HTML in string and wrap with code tag

我在处理文本内容中的HTML时遇到了麻烦。我正在考虑一种检测这些标签并将所有连续的标签包装在代码标签中的方法。

不要包裹我<p>Hello</p><div class="text">wrap me please!</div><span class="title">wrap me either!</span>不要包裹我<h1>End</h1>.

预期结果

不要包裹我<code><p>Hello</p><div class="text">wrap me please!</div><span class="title">wrap me either!</span></code>不要包裹我<code><h1>End</h1></code>.

这可能吗?

在这种特定情况下很难使用 DOMDocument,因为它会自动用 <p> 标签包装文本节点(并添加 doctype、head、html)。一种方法是使用 (?(DEFINE)...) 特征和命名子模式将模式构造为词法分析器:

$html = <<<EOD
Don't wrap me<p>Hello</p><div class="text">wrap me please!</div><span class="title">wrap me either!</span> Don't wrap me <h1>End</h1>
EOD;
$pattern = <<<'EOD'
~
(?(DEFINE)
    (?<self>    < [^'W_]++ [^>]* > )
    (?<comment> <!-- (?>[^-]++|-(?!->))* -->)
    (?<cdata>   'Q<![CDATA['E (?>[^]]++|](?!]>))* ]]> )
    (?<text>    [^<]++ )
    (?<tag>
        < ([^'W_]++) [^>]* >
        (?> 'g<text> | 'g<tag> | 'g<self> | 'g<comment> | 'g<cdata> )*
        </ 'g{-1} >
    )
)
# main pattern
(?: 'g<tag> | 'g<self> | 'g<comment> | 'g<cdata> )+
~x
EOD;
$html = preg_replace($pattern, '<code>$0</code>', $html);
echo htmlspecialchars($html);

(?(DEFINE)..)功能允许将定义部分放在正则表达式模式中。这个定义部分和里面命名的子模式没有任何匹配,它们稍后将在主模式中使用。

(?<abcd> ...)定义了一个子模式,您可以稍后在 'g<abcd> 中重用。在上面的模式中,以这种方式定义的子模式是:

  • self:描述自关闭标签
  • 注释:对于 HTML 注释
  • CDATA
  • :用于 CDATA
  • 文本
  • :对于文本(所有不是标签、注释或 CDATA)
  • 标记:适用于非自闭合的 HTML 标记

自我
[^'W_]是一种在没有下划线的情况下获得'w的技巧。 [^'W]++表示标记名称,也用于tag子模式。
[^>]*意味着所有不是零次或更多次>的事情。

注释
(?>[^-]++|-(?!->))*描述了 html 注释中的所有可能内容:

(?>          # open an atomic group
    [^-]++   # all that is not a literal -, one or more times (possessive)
  |          # OR
    -        # a literal -
    (?!->)   # not followed by -> (negative lookahead)
)*           # close and repeat the group zero or more times 

CDATA
'Q..'E之间的所有字符都被视为文字字符,像[这样的特殊字符不需要转义。(这只是使模式更具可读性的技巧)。
CDATA 中允许的内容的描述方式与 html 注释中的内容描述方式相同。

正文
[^<]++所有字符,直到左尖括号或字符串的末尾。

标签
这是最令人讨厌的子模式。第 1 行和第 3 行是开始标记和结束标记。请注意,在第 1 行中,使用捕获组捕获标记名称。在第 3 行中,'g{-1} 是指与最后定义的捕获组匹配的内容("-1"表示"左侧的一个")。
第 2 行描述了开始标记和结束标记之间的可能内容。您可以看到,此描述不仅使用之前定义的子模式,还使用当前子模式本身来允许嵌套标记。

设置完所有项目并关闭定义部分后,您可以轻松编写主模式。

我在处理文本内容中的HTML时遇到了麻烦。

然后只需转义该文本:

echo htmlspecialchars($your_text_that_may_contain_html_code);

用正则表达式解析 HTML 是一个众所周知的大不!

这将找到标签及其结束标签,以及介于两者之间的所有内容:

<[A-Z][A-Z0-9]*'b[^>]*>.*?</'1>

您也许能够捕获这些标签,并将其替换为它们周围的标签。 它可能不适用于每种情况,但如果 html 相当静态,您可能会发现它足以满足您的需求。