我需要匹配所有这些开头标记:

<p>
<a href="foo">

但不是这些:

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

我想出了这个,想确保我做得对。我只是在捕捉a-z。

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

我相信上面写着:

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

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


当前回答

免责声明:如果您有选择,请使用解析器。那是说。。。

这是我使用(!)匹配HTML标记的正则表达式:

<(?:"[^"]*"['"]*|'[^']*'['"]*|[^'">])+>

它可能并不完美,但我通过大量HTML运行了这段代码。注意,它甚至会捕捉到一些奇怪的东西,比如出现在网络上的<a name=“badgenerator”“>。

我想,为了使其不匹配自包含的标签,您可能需要使用Kobi的负面外观:

<(?:"[^"]*"['"]*|'[^']*'['"]*|[^'">])+(?<!/\s*)>

或者如果没有,就合并。

对于下选民:这是从实际产品中工作的代码。我怀疑任何阅读此页面的人都会觉得在HTML上使用正则表达式是社会可以接受的。

注意:我应该注意,这个正则表达式在CDATA块、注释以及脚本和样式元素的存在下仍然会崩溃。好消息是,你可以去掉那些使用正则表达式的。。。

其他回答

<?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库中,抓取所有元素,循环并过滤掉不自关闭的并对其进行操作。

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

我想这可能有用

<[a-z][^<>]*(?:(?:[^/]\s*)|(?:\s*[^/]))>

这可以在这里进行测试。


根据W3学校。。。

XML命名规则

XML元素必须遵循以下命名规则:

名称可以包含字母、数字和其他字符名称不能以数字或标点字符开头名称不能以字母xml(或xml、xml等)开头名称不能包含空格可以使用任何名称,不保留任何单词。


我使用的模式将遵循这些规则。

Try:

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

它与您的类似,但最后一个>不能在斜杠之后,也接受h1。

关于解析(x)HTML的正则表达式方法的问题,所有提到一些限制的人的答案都是:你没有受过足够的训练来统治这一强大武器的力量,因为这里没有人谈到递归。

一位正则表达式不可知论的同事通知了我这次讨论,这肯定不是网络上第一次讨论这个古老而热门的话题。

在阅读了一些帖子后,我做的第一件事就是在这个线程中查找“?R”字符串。第二个是搜索“递归”。

不,天哪,找不到火柴。由于没有人提到解析器构建的主要机制,我很快就意识到没有人理解这一点。

如果(x)HTML解析器需要递归,那么仅使用没有递归的正则表达式解析器是不够的。这是一个简单的构造。

正则表达式的黑色艺术很难掌握,因此,在尝试和测试我们的个人解决方案以一手掌握整个网络时,我们可能还遗漏了其他可能性。。。嗯,我很确定:)

这是一个神奇的模式:

$pattern = "/<([\w]+)([^>]*?)(([\s]*\/>)|(>((([^<]*?|<\!\-\-.*?\-\->)|(?R))*)<\/\\1[\s]*>))/s";

试试看。它是以PHP字符串形式编写的,所以“s”修饰符使类包含换行符。

下面是我在一月份编写的PHP手册的示例注释:参考

(注意。在那个注释中,我错误地使用了“m”修饰符;它应该被删除,尽管它被正则表达式引擎丢弃,因为没有使用^或$锚定)。

现在,我们可以从一个更明智的角度来讨论这种方法的局限性:

根据正则表达式引擎的具体实现,递归在解析嵌套模式的数量上可能有限制,但这取决于所使用的语言尽管已损坏,(x)HTML不会导致严重错误。它没有经过消毒。

无论如何,它只是一个正则表达式模式,但它揭示了开发许多强大实现的可能性。

我编写这个模式是为了支持我在框架中构建的模板引擎的递归下降解析器,无论是在执行时间还是在内存使用方面,性能都非常出色(与使用相同语法的其他模板引擎无关)。

虽然不能用正则表达式解析HTML的答案是正确的,但它们在这里并不适用。OP只想用正则表达式解析一个HTML标记,这可以用正则表达式完成。

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

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

如果你在正则表达式中添加了一些东西,通过回溯,它可能会被强制匹配像<a>>这样的愚蠢的东西,[^/]太宽容了。还要注意,<space>*[^/]*是冗余的,因为[^/]*也可以匹配空格。

我的建议是

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

其中(?<!…)是(在Perl正则表达式中)后面的否定外观。它的意思是“a<,然后是一个单词,然后是任何不是a>的东西,最后一个可能不是a/,后面是>”。

请注意,这允许使用<a/>(就像原始的正则表达式一样),因此如果您需要更严格的限制,则需要构建正则表达式以匹配由空格分隔的属性对。