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]+) *[^/]*?>
如果你在正则表达式中添加一些东西,通过回溯可以强制它匹配愚蠢的东西,比如<a >>
,[^/]
太宽松了。还要注意<space>*[^/]*
是多余的,因为[^/]*
也可以匹配空格。
我的建议是
<([a-z]+)[^>]*(?<!/)>
(?<! ... )
是(在 Perl 正则表达式中)否定的后视。它读作“一个的东西,最后一个可能不是/,然后是>”。
请注意,这允许 <a/ >
之类的内容(就像原始的正则表达式一样),因此如果您想要更严格的内容,您需要构建一个正则表达式来匹配由空格分隔的属性对。
【讨论】:
+1 指出问题不在于解析完整的 (X)HTML,而在于匹配 (X)HTML 开放标签。 其他大多数答案似乎都忽略了,即 HTML 解析器可以很好地在其实现中使用正则表达式来处理部分 HTML,如果大多数解析器不这样做,我会感到惊讶. 当属性值包含“>”或“/”字符时,此处给出的答案将失败。 这将在包含 cmets 或 CData 部分的 HTML 上无法正常工作。如果带引号的属性包含>
字符,它也将无法正常工作。我同意 OP 的建议 可以 用正则表达式来完成,但这里介绍的过于简单化了。
<h1>
标签想和你说一句话(我知道,很容易修复,但仍然如此)...【参考方案3】:
试试:
<([^\s]+)(\s[^>]*?)?(?<!/)>
和你的类似,但最后一个&gt;
不能在斜线之后,也接受h1
。
【讨论】:
哎呀&gt;
在属性值中有效。事实上,在“规范 XML”序列化中,您不能使用 &gt;
。 (这并不完全相关,只是要强调属性值中的&gt;
一点也不稀奇。)
@Kobi: 正则表达式中的感叹号(你放在末尾的那个)是什么意思?
@bobince:你确定吗?我不明白了,这也是有效的 HTML:<div title="this tag is a <div></div>">hello</div>
@MarcoDemaio - &gt;
不必在属性值中转义,但 <
可以。所以这将是有效的 HTML:<div title="this tag is a &lt;div>&lt;/div>">hello</div>
【参考方案4】:
我不知道您对此的确切需求,但如果您也在使用 .NET,您不能使用 Html Agility Pack 吗?
摘录:
这是一个 .NET 代码库,允许 您解析“网络之外”的 HTML 文件。解析器非常宽容 使用“真实世界”格式错误的 HTML。
【讨论】:
CodePlex 关闭(但这个在 CodePlex 存档中)。也许更新?【参考方案5】:我之前使用了一个名为HTMLParser 的开源工具。它旨在以各种方式解析 HTML 并很好地服务于目的。它可以将 HTML 解析为不同的树节点,您可以轻松地使用它的 API 从节点中获取属性。检查一下,看看这是否对您有帮助。
【讨论】:
【参考方案6】:在我看来,您正在尝试匹配末尾没有“/”的标签。试试这个:
<([a-zA-Z][a-zA-Z0-9]*)[^>]*(?<!/)>
【讨论】:
这不起作用。对于输入 '<?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 自包含标签除外的主要内容,如果未能解决你的问题,请参考以下文章