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

<p>
<a href="foo">

但不是这些:

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

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

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

我相信上面写着:

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

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


当前回答

尽管为此目的使用正则表达式并不合适和有效,但有时正则表达式为简单的匹配问题提供了快速解决方案,在我看来,将正则表达式用于琐碎的工作并不可怕。

有一篇关于匹配StevenLevithan编写的最内部HTML元素的权威博客文章。

其他回答

如果PHP需要此功能:

除非是正确格式化的XML,否则PHP DOM函数将无法正常工作。不管它们对人类其他人的用处有多大。

simplehtmldom很好,但我发现它有点bug,而且内存很重[会在大页面上崩溃。]

我从未使用过querypath,所以不能评论它的有用性。

另一个可以尝试的是我的DOMParser,它非常节省资源,我已经很高兴地使用了一段时间。简单易学,功能强大。

对于Python和Java,发布了类似的链接。

对于反对者,我只在XML解析器无法承受实际使用时才编写了我的课程。宗教上的反对投票只是阻止了有用的答案被发布——请把事情放在问题的角度之内。

下面是XML/XTML的PCRE正则表达式,它是根据简化的EBNF语法定义构建的:

/
(?(DEFINE)
(?<tag> (?&tagempty) | (?&tagopen) ((?&textnode) | (?&tag) | (?&comment))* (?&tagclose))
(?<tagunnested> (?&tagempty) | (?&tagopen) ((?&textnode) | (?&comment))* (?&tagclose))
(?<textnode> [^<>]+)
(?<comment> <!--([\s\S]*?)-->)
(?<tagopen> < (?&tagname) (?&attrlist)? (?&ws)* >)
(?<tagempty> < (?&tagname) (?&ws)* (?&attrlist)? (?&ws)* \/>)
(?<tagclose> <\/ (?&tagname) (?&ws)* >)
(?<attrlist> ((?&ws)+ (?&attr))+)
(?<attr> (?&attrunquoted) | (?&attrsinglequoted) | (?&attrdoublequoted) | (?&attrempty))
(?<attrempty> (?&attrname))
(?<attrunquoted> (?&attrname) (?&ws)* = (?&ws)* (?&attrunquotedvalue))
(?<attrsinglequoted> (?&attrname) (?&ws)* = (?&ws)* ' (?&attrsinglequotedvalue) ')
(?<attrdoublequoted> (?&attrname) (?&ws)* = (?&ws)* " (?&attrdoublequotedvalue) ")
(?<tagname> (?&alphabets) ((?&alphabets) | (?&digits))*)
(?<attrname>(?&alphabets)+((?&alphabets)|(?&digits)|[:-]) )
(?<attrunquotedvalue> [^\s"'=<>`]+)
(?<attrsinglequotedvalue> [^']+)
(?<attrdoublequotedvalue> [^"]+)
(?<alphabets> [a-zA-Z])
(?<digits> [0-9])
(?<ws> \s)
)
(?&tagopen)
/x

这说明了如何为上下文无关语法构建正则表达式。您可以通过将最后一行的匹配从(?&tagopen)更改为例如(?&tagunnested)来匹配定义的其他部分

真正的问题是:你应该这样做吗?

对于XML/XTML,共识是否定的!

感谢尼基奇提供了这个想法。

我想这可能有用

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

这可以在这里进行测试。


根据W3学校。。。

XML命名规则

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

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


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

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

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

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

各部分解释如下:

<:起始字符

\s*:标签名称前可能有空格(难看,但可能)。

(\w+):标记可以包含字母和数字(h1)。嗯,w也与“_”匹配,但我想它不会伤害你。如果好奇,请改用([a-zA-Z0-9]+)。

[^/>]*:除了>和/直到关闭>

>:关闭>

不相关的

对于那些低估了正则表达式的人来说,他们说正则表达式的功能和正则语言一样强大:

anbanban不是规则的,甚至不是上下文无关的,可以与^(a+)b\1b\1匹配$

反向引用FTW!