我需要匹配所有这些开头标记:
<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一次或多次,然后找到零个或多个空格,然后查找任何字符零次或多次,贪婪,除了/,然后查找大于
我有这个权利吗?更重要的是,你怎么看?
当前回答
有些人会告诉你地球是圆的(如果他们想用奇怪的话,也许地球是一个扁球体)。他们在撒谎。
有些人会告诉你正则表达式不应该是递归的。他们在限制你。他们需要征服你,他们让你蒙在鼓里。
你可以生活在他们的现实中,也可以服用红色药丸。
就像Marshal勋爵(他是Marshal.NET类的亲戚吗?)一样,我也看过Underverse Stack Based Regex Verse,并以你无法想象的力量知识返回。是的,我想有一两个老一号在保护他们,但他们在电视上看足球,所以这并不难。
我认为XML的情况非常简单。RegEx(在.NET语法中)是用base64压缩和编码的,以使你脆弱的头脑更容易理解,应该是这样的:
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
+r/vD34mUADO1P4/AQAA//8=
要设置的选项是RegexOptions.ExplicitCapture。您要查找的捕获组是ELEMENTNAME。如果捕获组ERROR不为空,则存在解析错误,Regex停止。
如果您在将其重新转换为人类可读的正则表达式时遇到问题,这将有助于:
static string FromBase64(string str)
{
byte[] byteArray = Convert.FromBase64String(str);
using (var msIn = new MemoryStream(byteArray))
using (var msOut = new MemoryStream()) {
using (var ds = new DeflateStream(msIn, CompressionMode.Decompress)) {
ds.CopyTo(msOut);
}
return Encoding.UTF8.GetString(msOut.ToArray());
}
}
如果你不确定,不,我不是在开玩笑(但也许我在撒谎)。它会奏效的。我构建了大量的单元测试来测试它,我甚至使用了(部分)一致性测试。它是一个标记化器,而不是一个完整的解析器,因此它只会将XML拆分为其组件标记。它不会解析/集成DTD。
哦如果需要正则表达式的源代码,请使用一些辅助方法:
正则表达式,用于标记xml或纯正则表达式
其他回答
在我看来,你试图匹配结尾没有“/”的标签。试试看:
<([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]+)(?:[^>]*[^/]*)?>
<\s*(\w+)[^/>]*>
各部分解释如下:
<:起始字符
\s*:标签名称前可能有空格(难看,但可能)。
(\w+):标记可以包含字母和数字(h1)。嗯,w也与“_”匹配,但我想它不会伤害你。如果好奇,请改用([a-zA-Z0-9]+)。
[^/>]*:除了>和/直到关闭>
>:关闭>
不相关的
对于那些低估了正则表达式的人来说,他们说正则表达式的功能和正则语言一样强大:
anbanban不是规则的,甚至不是上下文无关的,可以与^(a+)b\1b\1匹配$
反向引用FTW!
关于解析(x)HTML的正则表达式方法的问题,所有提到一些限制的人的答案都是:你没有受过足够的训练来统治这一强大武器的力量,因为这里没有人谈到递归。
一位正则表达式不可知论的同事通知了我这次讨论,这肯定不是网络上第一次讨论这个古老而热门的话题。
在阅读了一些帖子后,我做的第一件事就是在这个线程中查找“?R”字符串。第二个是搜索“递归”。
不,天哪,找不到火柴。由于没有人提到解析器构建的主要机制,我很快就意识到没有人理解这一点。
如果(x)HTML解析器需要递归,那么仅使用没有递归的正则表达式解析器是不够的。这是一个简单的构造。
正则表达式的黑色艺术很难掌握,因此,在尝试和测试我们的个人解决方案以一手掌握整个网络时,我们可能还遗漏了其他可能性。。。嗯,我很确定:)
这是一个神奇的模式:
$pattern = "/<([\w]+)([^>]*?)(([\s]*\/>)|(>((([^<]*?|<\!\-\-.*?\-\->)|(?R))*)<\/\\1[\s]*>))/s";
试试看。它是以PHP字符串形式编写的,所以“s”修饰符使类包含换行符。
下面是我在一月份编写的PHP手册的示例注释:参考
(注意。在那个注释中,我错误地使用了“m”修饰符;它应该被删除,尽管它被正则表达式引擎丢弃,因为没有使用^或$锚定)。
现在,我们可以从一个更明智的角度来讨论这种方法的局限性:
根据正则表达式引擎的具体实现,递归在解析嵌套模式的数量上可能有限制,但这取决于所使用的语言尽管已损坏,(x)HTML不会导致严重错误。它没有经过消毒。
无论如何,它只是一个正则表达式模式,但它揭示了开发许多强大实现的可能性。
我编写这个模式是为了支持我在框架中构建的模板引擎的递归下降解析器,无论是在执行时间还是在内存使用方面,性能都非常出色(与使用相同语法的其他模板引擎无关)。
我同意解析XML,特别是HTML的正确工具是解析器,而不是正则表达式引擎。然而,正如其他人所指出的,有时使用正则表达式更快、更容易,并且如果您知道数据格式,就可以完成任务。
微软实际上有一节《.NET Framework中正则表达式的最佳实践》,专门讨论了“考虑输入源”。
正则表达式确实有局限性,但您是否考虑过以下问题?
在正则表达式方面,.NET框架是独一无二的,因为它支持平衡组定义。
请参见将平衡构造与.NET正则表达式匹配请参见.NET正则表达式:Regex和平衡匹配请参阅Microsoft关于平衡组定义的文档
因此,我相信您可以使用正则表达式解析XML。然而,请注意,它必须是有效的XML(浏览器对HTML非常宽容,并且允许HTML中有错误的XML语法)。这是可能的,因为“平衡组定义”将允许正则表达式引擎充当PDA。
引用上述第1条:
.NET正则表达式引擎如上所述,不能用正则表达式。但是,.NET正则表达式引擎提供了一些允许平衡构造辨识。(?<group>)-使用名称组。(?<-group>)-从捕获堆栈。(?(组)yes|no)-如果存在组,则匹配yes部分否则,名称组不匹配任何部分。这些构造允许.NET正则表达式模拟通过本质上允许简单版本的堆栈来限制PDA操作:推送、弹出和清空。简单的操作非常简单分别相当于递增、递减和比较为零。这允许.NET正则表达式引擎识别上下文无关语言的子集,特别是那些仅需要一个简单的计数器。这反过来允许非传统的.NET正则表达式,以识别各个正确平衡的构造。
考虑以下正则表达式:
(?=<ul\s+id="matchMe"\s+type="square"\s*>)
(?>
<!-- .*? --> |
<[^>]*/> |
(?<opentag><(?!/)[^>]*[^/]>) |
(?<-opentag></[^>]*[^/]>) |
[^<>]*
)*
(?(opentag)(?!))
使用标志:
单线IgnorePatternHitespace(如果折叠正则表达式并删除所有空格,则不需要)IgnoreCase(不需要)
正则表达式解释(内联)
(?=<ul\s+id="matchMe"\s+type="square"\s*>) # match start with <ul id="matchMe"...
(?> # atomic group / don't backtrack (faster)
<!-- .*? --> | # match xml / html comment
<[^>]*/> | # self closing tag
(?<opentag><(?!/)[^>]*[^/]>) | # push opening xml tag
(?<-opentag></[^>]*[^/]>) | # pop closing xml tag
[^<>]* # something between tags
)* # match as many xml tags as possible
(?(opentag)(?!)) # ensure no 'opentag' groups are on stack
您可以在一个更好的.NET正则表达式测试仪上尝试。
我使用了以下示例源:
<html>
<body>
<div>
<br />
<ul id="matchMe" type="square">
<li>stuff...</li>
<li>more stuff</li>
<li>
<div>
<span>still more</span>
<ul>
<li>Another >ul<, oh my!</li>
<li>...</li>
</ul>
</div>
</li>
</ul>
</div>
</body>
</html>
这找到了匹配项:
<ul id="matchMe" type="square">
<li>stuff...</li>
<li>more stuff</li>
<li>
<div>
<span>still more</span>
<ul>
<li>Another >ul<, oh my!</li>
<li>...</li>
</ul>
</div>
</li>
</ul>
尽管它实际上是这样的:
<ul id="matchMe" type="square"> <li>stuff...</li> <li>more stuff</li> <li> <div> <span>still more</span> <ul> <li>Another >ul<, oh my!</li> <li>...</li> </ul> </div> </li> </ul>
最后,我真的很喜欢杰夫·阿特伍德的文章:解析Html的Cthhulhu方式。有趣的是,它引用了这个问题的答案,目前有超过4万张选票。