我需要匹配所有这些开头标记:
<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一次或多次,然后找到零个或多个空格,然后查找任何字符零次或多次,贪婪,除了/,然后查找大于
我有这个权利吗?更重要的是,你怎么看?
当前回答
下面是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,共识是否定的!
感谢尼基奇提供了这个想法。
其他回答
下面是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,共识是否定的!
感谢尼基奇提供了这个想法。
虽然不能用正则表达式解析HTML的答案是正确的,但它们在这里并不适用。OP只想用正则表达式解析一个HTML标记,这可以用正则表达式完成。
不过,建议的正则表达式是错误的:
<([a-z]+) *[^/]*?>
如果你在正则表达式中添加了一些东西,通过回溯,它可能会被强制匹配像<a>>这样的愚蠢的东西,[^/]太宽容了。还要注意,<space>*[^/]*是冗余的,因为[^/]*也可以匹配空格。
我的建议是
<([a-z]+)[^>]*(?<!/)>
其中(?<!…)是(在Perl正则表达式中)后面的否定外观。它的意思是“a<,然后是一个单词,然后是任何不是a>的东西,最后一个可能不是a/,后面是>”。
请注意,这允许使用<a/>(就像原始的正则表达式一样),因此如果您需要更严格的限制,则需要构建正则表达式以匹配由空格分隔的属性对。
中国古代军事家、将军和哲学家孙子说过:
据说,如果你了解你的敌人,了解你自己,你可以赢得百战百胜,而不会一败涂地。如果你只了解自己,而不了解对手,你可能会赢,也可能会输。如果你既不了解你自己也不了解你的敌人,你将永远危及自己。
在这种情况下,你的敌人是HTML,你要么是你自己,要么是正则表达式。您甚至可能是带有不规则正则表达式的Perl。了解HTML。了解自己。
我写了一首描述HTML本质的俳句。
HTML has
complexity exceeding
regular language.
我还写了一首俳句,描述Perl中正则表达式的性质。
The regex you seek
is defined within the phrase
<([a-zA-Z]+)(?:[^>]*[^/]*)?>
我不知道你对此的确切需求,但如果你也在使用.NET,你就不能使用Html Agility Pack吗?
摘录:
它是一个.NET代码库,允许您可以解析“网页外”HTML文件夹。解析器非常宽容具有“真实世界”格式错误的HTML。
每当我需要从HTML文档中快速提取内容时,我都会使用Tidy将其转换为XML,然后使用XPath或XSLT来获取所需内容。在您的案例中,类似于:
//p/a[@href='foo']