RegEx 匹配打开的标签,XHTML 自包含标签除外

Posted

技术标签:

【中文标题】RegEx 匹配打开的标签,XHTML 自包含标签除外【英文标题】:RegEx match open tags except XHTML self-contained tags 【发布时间】:2010-12-16 11:36:23 【问题描述】:

我需要匹配所有这些开始标签:

<p>
<a href="foo">

但不是这些:

<br />
<hr class="foo" />

我想出了这个并想确保我做对了。我只捕获a-z

<([a-z]+) *[^/]*?>

我相信它说:

找一个小于,然后 查找(并捕获)a-z 一次或多次,然后 找到零个或多个空格,然后 找到任意字符零次或多次,贪心,除了/,然后 查找大于

我有这个权利吗?更重要的是,你怎么看?

【问题讨论】:

【参考方案1】:

正如许多人已经指出的那样,html 不是一种常规语言,因此很难解析。我对此的解决方案是使用整洁的程序将其转换为常规语言,然后使用 XML 解析器来使用结果。有很多很好的选择。我的程序是使用带有jtidy 库的Java 编写的,用于将HTML 转换为XML,然后将Jaxen 转换为xpath 为结果。

【讨论】:

【参考方案2】:

虽然您无法使用正则表达式解析 HTML 的答案是正确的,但它们并不适用于此。 OP 只想用正则表达式解析一个 HTML 标记,而这可以通过正则表达式完成。

建议的正则表达式是错误的:

<([a-z]+) *[^/]*?>

如果你在正则表达式中添加一些东西,通过回溯可以强制它匹配愚蠢的东西,比如&lt;a &gt;&gt;[^/] 太宽松了。还要注意&lt;space&gt;*[^/]* 是多余的,因为[^/]* 也可以匹配空格。

我的建议是

<([a-z]+)[^>]*(?<!/)>

(?&lt;! ... ) 是(在 Perl 正则表达式中)否定的后视。它读作“一个的东西,最后一个可能不是/,然后是>”。

请注意,这允许 &lt;a/ &gt; 之类的内容(就像原始的正则表达式一样),因此如果您想要更严格的内容,您需要构建一个正则表达式来匹配由空格分隔的属性对。

【讨论】:

+1 指出问题不在于解析完整的 (X)HTML,而在于匹配 (X)HTML 开放标签。 其他大多数答案似乎都忽略了,即 HTML 解析器可以很好地在其实现中使用正则表达式来处理部分 HTML,如果大多数解析器不这样做,我会感到惊讶. 当属性值包含“>”或“/”字符时,此处给出的答案将失败。 这将在包含 cmets 或 CData 部分的 HTML 上无法正常工作。如果带引号的属性包含&gt; 字符,它也将无法正常工作。我同意 OP 的建议 可以 用正则表达式来完成,但这里介绍的过于简单化了。 &lt;h1&gt; 标签想和你说一句话(我知道,很容易修复,但仍然如此)...【参考方案3】:

试试:

<([^\s]+)(\s[^>]*?)?(?<!/)>

和你的类似,但最后一个&amp;gt;不能在斜线之后,也接受h1

【讨论】:

哎呀 &amp;gt; 在属性值中有效。事实上,在“规范 XML”序列化中,您不能使用 &amp;gt;。 (这并不完全相关,只是要强调属性值中的&amp;gt; 一点也不稀奇。) @Kobi: 正则表达式中的感叹号(你放在末尾的那个)是什么意思? @bobince:你确定吗?我不明白了,这也是有效的 HTML:&lt;div title="this tag is a &lt;div&gt;&lt;/div&gt;"&gt;hello&lt;/div&gt; @MarcoDemaio - &amp;gt; 不必在属性值中转义,但 &lt; 可以。所以这将是有效的 HTML:&lt;div title="this tag is a &amp;lt;div&gt;&amp;lt;/div&gt;"&gt;hello&lt;/div&gt;【参考方案4】:

我不知道您对此的确切需求,但如果您也在使用 .NET,您不能使用 Html Agility Pack 吗?

摘录:

这是一个 .NET 代码库,允许 您解析“网络之外”的 HTML 文件。解析器非常宽容 使用“真实世界”格式错误的 HTML。

【讨论】:

CodePlex 关闭(但这个在 CodePlex 存档中)。也许更新?【参考方案5】:

我之前使用了一个名为HTMLParser 的开源工具。它旨在以各种方式解析 HTML 并很好地服务于目的。它可以将 HTML 解析为不同的树节点,您可以轻松地使用它的 API 从节点中获取属性。检查一下,看看这是否对您有帮助。

【讨论】:

【参考方案6】:

在我看来,您正在尝试匹配末尾没有“/”的标签。试试这个:

<([a-zA-Z][a-zA-Z0-9]*)[^>]*(?<!/)>

【讨论】:

这不起作用。对于输入 '' 匹配的是 x 和 y,尽管 x 已终止。【参考方案7】:
<?php
$selfClosing = explode(',', 'area,base,basefont,br,col,frame,hr,img,input,isindex,link,meta,param,embed');

$html = '
<p><a href="#">foo</a></p>
<hr/>
<br/>
<div>name</div>';

$dom = new DOMDocument();
$dom->loadHTML($html);
$els = $dom->getElementsByTagName('*');
foreach ( $els as $el ) 
    $nodeName = strtolower($el->nodeName);
    if ( !in_array( $nodeName, $selfClosing ) ) 
        var_dump( $nodeName );
    

输出:

string(4) "html"
string(4) "body"
string(1) "p"
string(1) "a"
string(3) "div"

基本上只定义自闭的元素节点名称,将整个 html 字符串加载到 DOM 库中,抓取所有元素,循环并过滤掉非自闭的元素并对其进行操作。

我相信你现在已经知道你不应该为此使用正则表达式。

【讨论】:

如果你正在处理真正的 XHTML,那么在 getElementsByTagName 后面加上 NS 并指定命名空间。

以上是关于RegEx 匹配打开的标签,XHTML 自包含标签除外的主要内容,如果未能解决你的问题,请参考以下文章

php 用于匹配ID标签的REGEX

RegEx 只查看 HTML 标签内的文本?

XHTML标签的嵌套规则分析

从 xhtml 文档中删除未关闭的打开 <p> 标签

正则表达式将文件夹名称与Productivity Power Tools颜色编码匹配

如何为提及和主题标签修复此正则表达式?