我需要匹配所有这些开头标记:
<p>
<a href="foo">
但不是这些:
<br />
<hr class="foo" />
我想出了这个,想确保我做得对。我只是在捕捉a-z。
<([a-z]+) *[^/]*?>
我相信上面写着:
找到一个小于,然后查找(并捕获)a-z一次或多次,然后找到零个或多个空格,然后查找任何字符零次或多次,贪婪,除了/,然后查找大于
我有这个权利吗?更重要的是,你怎么看?
我需要匹配所有这些开头标记:
<p>
<a href="foo">
但不是这些:
<br />
<hr class="foo" />
我想出了这个,想确保我做得对。我只是在捕捉a-z。
<([a-z]+) *[^/]*?>
我相信上面写着:
找到一个小于,然后查找(并捕获)a-z一次或多次,然后找到零个或多个空格,然后查找任何字符零次或多次,贪婪,除了/,然后查找大于
我有这个权利吗?更重要的是,你怎么看?
当前回答
在我看来,你试图匹配结尾没有“/”的标签。试试看:
<([a-zA-Z][a-zA-Z0-9]*)[^>]*(?<!/)>
其他回答
中国古代军事家、将军和哲学家孙子说过:
据说,如果你了解你的敌人,了解你自己,你可以赢得百战百胜,而不会一败涂地。如果你只了解自己,而不了解对手,你可能会赢,也可能会输。如果你既不了解你自己也不了解你的敌人,你将永远危及自己。
在这种情况下,你的敌人是HTML,你要么是你自己,要么是正则表达式。您甚至可能是带有不规则正则表达式的Perl。了解HTML。了解自己。
我写了一首描述HTML本质的俳句。
HTML has
complexity exceeding
regular language.
我还写了一首俳句,描述Perl中正则表达式的性质。
The regex you seek
is defined within the phrase
<([a-zA-Z]+)(?:[^>]*[^/]*)?>
免责声明:如果您有选择,请使用解析器。那是说。。。
这是我使用(!)匹配HTML标记的正则表达式:
<(?:"[^"]*"['"]*|'[^']*'['"]*|[^'">])+>
它可能并不完美,但我通过大量HTML运行了这段代码。注意,它甚至会捕捉到一些奇怪的东西,比如出现在网络上的<a name=“badgenerator”“>。
我想,为了使其不匹配自包含的标签,您可能需要使用Kobi的负面外观:
<(?:"[^"]*"['"]*|'[^']*'['"]*|[^'">])+(?<!/\s*)>
或者如果没有,就合并。
对于下选民:这是从实际产品中工作的代码。我怀疑任何阅读此页面的人都会觉得在HTML上使用正则表达式是社会可以接受的。
注意:我应该注意,这个正则表达式在CDATA块、注释以及脚本和样式元素的存在下仍然会崩溃。好消息是,你可以去掉那些使用正则表达式的。。。
下面是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,共识是否定的!
感谢尼基奇提供了这个想法。
解决方案如下:
<?php
// here's the pattern:
$pattern = '/<(\w+)(\s+(\w+)\s*\=\s*(\'|")(.*?)\\4\s*)*\s*(\/>|>)/';
// a string to parse:
$string = 'Hello, try clicking <a href="#paragraph">here</a>
<br/>and check out.<hr />
<h2>title</h2>
<a name ="paragraph" rel= "I\'m an anchor"></a>
Fine, <span title=\'highlight the "punch"\'>thanks<span>.
<div class = "clear"></div>
<br>';
// let's get the occurrences:
preg_match_all($pattern, $string, $matches, PREG_PATTERN_ORDER);
// print the result:
print_r($matches[0]);
?>
为了深入测试,我输入了字符串自动关闭标记,如:
<hr/><br/><br>
我还输入了标记:
一个属性多个属性值绑定到单引号或双引号的属性分隔符为双引号时包含单引号的属性,反之亦然在“=”符号之前、之后以及前后都有空格的“unputy”属性。
如果你在上面的概念证明中发现了不起作用的东西,我可以分析代码来提高我的技能。
<编辑>我忘记了用户的问题是避免解析自动关闭标签。在这种情况下,模式更简单,变为:
$pattern = '/<(\w+)(\s+(\w+)\s*\=\s*(\'|")(.*?)\\4\s*)*\s*>/';
用户@ridgerunner注意到,该模式不允许未加引号的属性或没有值的属性。在这种情况下,微调会带来以下模式:
$pattern = '/<(\w+)(\s+(\w+)(\s*\=\s*(\'|"|)(.*?)\\5\s*)?)*\s*>/';
</EDIT>
了解模式
如果有人有兴趣了解更多有关模式的信息,我会提供一些提示:
第一个子表达式(\w+)与标记名匹配第二个子表达式包含属性的模式。其组成如下:一个或多个空白区+属性的名称(\w+)零个或多个空格\s*(是否可能,此处留空)“=”符号同样,零个或多个空白属性值的分隔符,单引号或双引号(“|”)。在模式中,单引号被转义,因为它与PHP字符串分隔符重合。此子表达式用括号捕获,因此可以再次引用它来解析属性的闭包,这就是为什么它非常重要的原因。属性的值,几乎可以匹配:(.*?);在这个特定的语法中,使用贪婪匹配(星号后面的问号),RegExp引擎启用了一个类似“向前看”的运算符,它匹配除此子表达式后面的内容以外的任何内容有趣的是:\4部分是一个backreference运算符,它指的是模式中之前定义的子表达式,在本例中,我指的是第四个子表达式,它是找到的第一个属性分隔符零个或多个空格*属性子表达式在这里结束,指定了零个或多个可能出现的事件,用星号表示。然后,由于标记可能以“>”符号之前的空白结尾,因此零个或更多的空白与\s*子模式匹配。要匹配的标记可能以一个简单的“>”符号结尾,也可能以XHTML闭包结尾,这使用了前面的斜杠:(/>|>)。当然,斜线是转义的,因为它与正则表达式分隔符重合。
小提示:为了更好地分析这段代码,有必要查看生成的源代码,因为我没有提供任何HTML特殊字符转义。
我建议在PHP中使用QueryPath解析XML和HTML。它的语法与jQuery基本相同,只是在服务器端。