提问人: 提问时间:11/14/2009 最后编辑:11 revs, 7 users 58%Jeff 更新时间:9/15/2023 访问量:3723641
正则表达式匹配开放标记(XHTML 独立标记除外)
RegEx match open tags except XHTML self-contained tags
问:
我需要匹配所有这些开始标签:
<p>
<a href="foo">
但不是这些:
<br />
<hr class="foo" />
我想出了这个,并想确保我做对了。我只是捕获了.a-z
<([a-z]+) *[^/]*?>
我相信它说:
- 找到一个小于,然后
- 查找(并捕获)a-z一次或多次,然后
- 找到零个或多个空格,然后
- 查找任何字符零次或多次,贪婪,除了 ,然后
/
- 找到一个大于
我有这个权利吗?更重要的是,你怎么看?
答:
您希望第一个前面不要有 .请在此处查看有关如何执行此操作的详细信息。这被称为负面回溯。>
/
但是,该实现的幼稚实现最终将在此示例文档中匹配<bar/></foo>
<foo><bar/></foo>
您能提供有关您要解决的问题的更多信息吗?您是否正在以编程方式遍历标签?
尝试:
<([^\s]+)(\s[^>]*?)?(?<!/)>
它与你的类似,但最后一个不能在斜杠之后,并且也接受.>
h1
评论
>
在属性值中有效。事实上,在“规范 XML”序列化中,您不能使用 .(这并不完全相关,只是要强调在属性中值根本不是一件不寻常的事情。>
>
<div title="this tag is a <div></div>">hello</div>
>
<
<div title="this tag is a <div></div>">hello</div>
不能使用正则表达式解析 [X]HTML。因为 HTML 不能被正则表达式解析。正则表达式不是可用于正确解析 HTML 的工具。正如我之前在 HTML 和正则表达式问题中多次回答的那样,使用正则表达式将不允许你使用 HTML。正则表达式是一种不够复杂的工具,无法理解 HTML 使用的构造。HTML 不是常规语言,因此不能通过正则表达式解析。正则表达式查询无法将 HTML 分解为有意义的部分。这么多次了,但它没有找到我。即使是 Perl 使用的增强的不规则正则表达式也无法完成解析 HTML 的任务。你永远不会让我崩溃。HTML 是一种足够复杂的语言,它不能被正则表达式解析。即使是 Jon Skeet 也无法使用正则表达式解析 HTML。每次您尝试使用正则表达式解析 HTML 时,邪恶的孩子都会流着处女的鲜血,俄罗斯黑客会攻击您的 Web 应用程序。使用正则表达式解析 HTML 会召唤被污染的灵魂进入活人的领域。HTML 和正则表达式就像爱情、婚姻和仪式杀婴一样相辅相成。<中心>守不住为时已晚。正则表达式和 HTML 在同一个概念空间中的力量会像那么多水腻子一样摧毁你的思想。如果你用正则表达式解析 HTML,你就是在屈服于他们和他们的亵渎方式,这些方式注定了我们所有人为那个名字无法在基本多语言平面中表达的人进行不人道的辛劳,他来了。HTML-plus-regexp 会在你观察时液化有知觉者的神经,你的心灵在恐怖的冲击中枯萎。基于 Rege̿̔̉x 的 HTML 解析器是杀死 StackOverflow 的癌症,为时已晚,为时已晚,我们无法得救 chi͡ld 的违规行为确保正则表达式将消耗所有活组织(除了 HTML,它不能,正如之前预言的那样) 亲爱的主,帮助我们 任何人怎么能在这个祸害中幸存下来 使用正则表达式来解析 HTML 注定了人类将永远遭受可怕的折磨和安全漏洞 使用 regex 作为处理 HTML 的工具在这个世界和 c͒ͪo͛ͫrrupt 实体(如 SGML 实体,但更腐败)的可怕领域之间建立了一个 brea ch,仅仅瞥见了 HTML 的正则解析器世界,就会在不停地尖叫中传递一个 programmer 的意识,他来了, 瘟疫 slithy regex 感染将吞噬您的 HTML 解析器、应用程序和存在,就像 Visual Basic 一样,只会更糟的是,他来了,他来了,他来不适应he com̡e̶s, ̕h̵is un̨ho͞ly radiańcé destro҉ying all enli̍̈́̂̈́ghtenment, HTML tags lea͠ki̧n͘g fr̶ǫm ̡yo͟ur eye͢s̸ ̛l̕ik͏e liquid pain, re̸gular expression parsing 的歌曲将熄灭mor 的声音tal man from the sphere I can see it can you see ̲͚̖͔̙î̩́t̲͎̩̱͔́̋̀ it is beautifult he ffing ofthe lies s of Man ALL IS LOŚ͖̩͇̗̪̏̈́T ALL IS LOST the pon̷y he comes he c̶̮om eshe comes t he ich or permeates all MY FACE MY FACE oh god no no NOO̼OO NΘ stop thean*̶͑̾̾̅ͫ͏̙̤g͇̫͛͆̾ͫ̑͆l͉̗̩̳̟̍ͫͥͨe̠̅s not rè̑ͧ̌aͨl̘̝̙̃ͤ͂̾̆ ZA̡͊͠͝LGΌ ISͮ̂҉̯͈͕̹̘̱ to͇̹̺ͅƝ̴ȳ̳ TH̘ë͖́̉ ͠P̯͍ ̭O̚N̐Y̡ H̸̡̪̯ͨ͊̽̅̾̎Ȩ̬̩̾͛ͪ̈́̀́͘ ̶̧̨̱̹̭̯ͧ̾ͬC̷̙̲̝͖ͭ̏ͥͮ͟Oͮ͏̮̪̝͍M̲̖͊̒ͪͩͬ̚̚͜Ȇ̴̟̟͙̞ͩ͌͝ S̨̥̫͎̭ͯ̿̔̀ͅinal snuf
͎a̧͈͖r̽̾̈́͒͑e
您是否尝试过改用 XML 解析器?
主持人注
此帖子被锁定以防止对其内容进行不当编辑。该帖子看起来与它应该看起来完全一样 - 它的内容没有问题。请不要标记它以引起我们的注意。
评论
我建议使用 QueryPath 在 PHP 中解析 XML 和 HTML。它与jQuery的语法基本相同,只是它在服务器端。
评论
虽然只有正则表达式的任意 HTML 是不可能的,但有时使用它们来解析一组有限的已知 HTML 是合适的。
如果您有一小组 HTML 页面,您想从中抓取数据,然后将其塞入数据库,则正则表达式可能工作正常。例如,我最近想从议会的网站上获得澳大利亚联邦众议员的姓名、政党和选区。这是一项有限的一次性工作。
正则表达式对我来说效果很好,而且设置速度非常快。
评论
&foo;
CDATA
<font>
免责声明:如果可以选择,请使用解析器。可是。。。
这是我使用 (!) 来匹配 HTML 标签的正则表达式:
<(?:"[^"]*"['"]*|'[^']*'['"]*|[^'">])+>
它可能并不完美,但我通过很多 HTML 运行了这段代码。请注意,它甚至会捕捉到一些奇怪的东西,例如,这些东西出现在网络上。<a name="badgenerator"">
我想要使它与自包含的标签不匹配,您要么想使用 Kobi 的负面后视:
<(?:"[^"]*"['"]*|'[^']*'['"]*|[^'">])+(?<!/\s*)>
或者只是结合 if 和 if not。
致反对者:这是来自实际产品的工作代码。我怀疑任何阅读此页面的人都会觉得在 HTML 上使用正则表达式在社会上是可以接受的。
注意:我应该注意,在存在 CDATA 块、注释以及脚本和样式元素的情况下,此正则表达式仍然会崩溃。好消息是,您可以摆脱那些使用正则表达式的...
评论
<!doctype html><title><</title>
'<!doctype html><title><</title>'.match(/<(?:"[^"]*"['"]*|'[^']*'['"]*|[^'">])+>/g)
["<!doctype html>", "<title>", "<</title>"]
["<title>", "</title>"]
<pre>
<?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 库中,抓取所有元素,循环并过滤掉那些不自闭合的元素并对其进行操作。
我相信你现在已经知道你不应该为此目的使用正则表达式。
评论
NS
在我看来,您正在尝试匹配末尾没有“/”的标签。试试这个:
<([a-zA-Z][a-zA-Z0-9]*)[^>]*(?<!/)>
评论
我以前使用过一个名为 HTMLParser 的开源工具。它旨在以各种方式解析 HTML,并且可以很好地达到目的。它可以将 HTML 解析为不同的树节点,您可以轻松地使用其 API 从节点中获取属性。检查一下,看看这是否能帮到你。
如果你在PHP中需要这个:
除非 PHP DOM 函数格式正确,否则它将无法正常工作。无论它们的使用对其他人有多大好处。
simplehtmldom 很好,但我发现它有点错误,而且内存很重 [会在大页面上崩溃。
我从未使用过查询路径,所以无法评论它的用处。
另一个可以尝试的是我的 DOMParser,它对资源非常少,我已经愉快地使用了一段时间。简单易学且功能强大。
对于 Python 和 Java,也发布了类似的链接。
对于反对者 - 我只在XML解析器被证明无法承受实际使用时才编写我的类。宗教反对票只会阻止发布有用的答案 - 请保持问题的角度。
我不知道你对此的确切需求,但如果你也在使用 .NET,你不能使用 Html Agility Pack 吗?
摘录:
它是一个 .NET 代码库,允许 你要解析“网络外”的HTML 文件。解析器非常宽容 与“现实世界”格式错误的 HTML。
评论
每当我需要从 HTML 文档中快速提取内容时,我都会使用 Tidy 将其转换为 XML,然后使用 XPath 或 XSLT 来获取我需要的内容。 在你的情况下,是这样的:
//p/a[@href='foo']
我认为这里的缺陷是 HTML 是 Chomsky Type 2 语法(上下文无关语法),而正则表达式是 Chomsky Type 3 语法(常规语法)。由于 Type 2 语法从根本上比 Type 3 语法更复杂(参见 Chomsky 层次结构),因此您不可能做到这一点。
但许多人会尝试,有些人甚至会声称成功——但直到其他人发现错误并完全搞砸你。
评论
A -> s A e
虽然你不能用正则表达式解析 HTML 的答案是正确的,但它们在这里并不适用。OP 只想解析一个带有正则表达式的 HTML 标签,这是可以使用正则表达式完成的。
但是,建议的正则表达式是错误的:
<([a-z]+) *[^/]*?>
如果你在正则表达式中添加一些东西,通过回溯,它可能会被迫匹配愚蠢的东西,比如 ,太宽松了。另请注意,这是多余的,因为 can 也可以匹配空格。<a >>
[^/]
<space>*[^/]*
[^/]*
我的建议是
<([a-z]+)[^>]*(?<!/)>
哪里是(在Perl正则表达式中)否定的后视。它读作“一个<,然后是一个单词,然后是任何不是>的东西,最后一个可能不是 /,然后是 >”。(?<! ... )
请注意,这允许类似的东西(就像原始正则表达式一样),所以如果你想要一些更严格的内容,你需要构建一个正则表达式来匹配用空格分隔的属性对。<a/ >
评论
>
正如许多人已经指出的那样,HTML 不是一种常规语言,这使得它很难解析。我的解决方案是使用整洁的程序将其转换为常规语言,然后使用XML解析器来使用结果。这有很多不错的选择。我的程序是使用 Java 和 jtidy 库编写的,将 HTML 转换为 XML,然后将 Jaxen 到 xpath 转换为结果。
虽然为此目的使用正则表达式并不合适且有效,但有时正则表达式可以为简单的匹配问题提供快速解决方案,在我看来,将正则表达式用于琐碎的工作并不那么可怕。
Steven Levithan 撰写了一篇关于匹配最内层 HTML 元素的权威博客文章。
- 图灵.sed
- 编写HTML解析器(作业)
- ???
- 利润!
相关(为什么不应该使用正则表达式匹配):
评论
这可能做:
<.*?[^/]>
或者不带结束标签:
<[^/].*?[^/]>
HTML 解析器上的火焰战争是怎么回事?HTML 解析器必须先解析(并重新构建)整个文档,然后才能对搜索进行分类。在某些情况下,正则表达式可能更快/更优雅。我的 2 美分......
评论
<a href="foo" title="5>3"> Oops </a>
(引自@Gareth)
这里有一些不错的正则表达式可以用 BBCode 替换 HTML。对于所有反对者,请注意,他并不是试图完全解析 HTML,只是为了清理它。他可能有能力杀死他的简单“解析器”无法理解的标签。
例如:
$store =~ s/http:/http:\/\//gi;
$store =~ s/https:/https:\/\//gi;
$baseurl = $store;
if (!$query->param("ascii")) {
$html =~ s/\s\s+/\n/gi;
$html =~ s/<pre(.*?)>(.*?)<\/pre>/\[code]$2\[\/code]/sgmi;
}
$html =~ s/\n//gi;
$html =~ s/\r\r//gi;
$html =~ s/$baseurl//gi;
$html =~ s/<h[1-7](.*?)>(.*?)<\/h[1-7]>/\n\[b]$2\[\/b]\n/sgmi;
$html =~ s/<p>/\n\n/gi;
$html =~ s/<br(.*?)>/\n/gi;
$html =~ s/<textarea(.*?)>(.*?)<\/textarea>/\[code]$2\[\/code]/sgmi;
$html =~ s/<b>(.*?)<\/b>/\[b]$1\[\/b]/gi;
$html =~ s/<i>(.*?)<\/i>/\[i]$1\[\/i]/gi;
$html =~ s/<u>(.*?)<\/u>/\[u]$1\[\/u]/gi;
$html =~ s/<em>(.*?)<\/em>/\[i]$1\[\/i]/gi;
$html =~ s/<strong>(.*?)<\/strong>/\[b]$1\[\/b]/gi;
$html =~ s/<cite>(.*?)<\/cite>/\[i]$1\[\/i]/gi;
$html =~ s/<font color="(.*?)">(.*?)<\/font>/\[color=$1]$2\[\/color]/sgmi;
$html =~ s/<font color=(.*?)>(.*?)<\/font>/\[color=$1]$2\[\/color]/sgmi;
$html =~ s/<link(.*?)>//gi;
$html =~ s/<li(.*?)>(.*?)<\/li>/\[\*]$2/gi;
$html =~ s/<ul(.*?)>/\[list]/gi;
$html =~ s/<\/ul>/\[\/list]/gi;
$html =~ s/<div>/\n/gi;
$html =~ s/<\/div>/\n/gi;
$html =~ s/<td(.*?)>/ /gi;
$html =~ s/<tr(.*?)>/\n/gi;
$html =~ s/<img(.*?)src="(.*?)"(.*?)>/\[img]$baseurl\/$2\[\/img]/gi;
$html =~ s/<a(.*?)href="(.*?)"(.*?)>(.*?)<\/a>/\[url=$baseurl\/$2]$4\[\/url]/gi;
$html =~ s/\[url=$baseurl\/http:\/\/(.*?)](.*?)\[\/url]/\[url=http:\/\/$1]$2\[\/url]/gi;
$html =~ s/\[img]$baseurl\/http:\/\/(.*?)\[\/img]/\[img]http:\/\/$1\[\/img]/gi;
$html =~ s/<head>(.*?)<\/head>//sgmi;
$html =~ s/<object>(.*?)<\/object>//sgmi;
$html =~ s/<script(.*?)>(.*?)<\/script>//sgmi;
$html =~ s/<style(.*?)>(.*?)<\/style>//sgmi;
$html =~ s/<title>(.*?)<\/title>//sgmi;
$html =~ s/<!--(.*?)-->/\n/sgmi;
$html =~ s/\/\//\//gi;
$html =~ s/http:\//http:\/\//gi;
$html =~ s/https:\//https:\/\//gi;
$html =~ s/<(?:[^>'"]*|(['"]).*?\1)*>//gsi;
$html =~ s/\r\r//gi;
$html =~ s/\[img]\//\[img]/gi;
$html =~ s/\[url=\//\[url=/gi;
评论
关于解析(x)HTML的正则表达式方法的问题,所有谈到一些限制的人的答案是:你还没有受过足够的训练来统治这种强大武器的力量,因为这里没有人谈论递归。
一位与正则表达式无关的同事通知了我这个讨论,这肯定不是网络上第一个关于这个古老而热门的话题。
在阅读了一些帖子后,我做的第一件事就是寻找“?R“字符串。第二个是搜索“递归”。
不,天哪,没有找到匹配项。由于没有人提到构建解析器的主要机制,我很快意识到没有人明白这一点。
如果 (x)HTML 解析器需要递归,那么没有递归的正则表达式解析器是不够的。这是一个简单的结构。
正则表达式的黑色艺术很难掌握,所以也许我们在尝试和测试我们的个人解决方案以一只手捕获整个网络时遗漏了更多的可能性......好吧,我确信它:)
这是神奇的模式:
$pattern = "/<([\w]+)([^>]*?)(([\s]*\/>)|(>((([^<]*?|<\!\-\-.*?\-\->)|(?R))*)<\/\\1[\s]*>))/s";
试试吧。它被写成一个PHP字符串,所以“s”修饰符使类包含换行符。
这是我在一月份写的PHP手册的示例说明: 参考资料
(保重。在那张纸条中,我错误地使用了“m”修饰符;它应该被擦除,尽管它被正则表达式引擎丢弃,因为没有使用或锚定)。^
$
现在,我们可以从更明智的角度来谈论这种方法的局限性:
- 根据正则表达式引擎的具体实现,递归在解析的嵌套模式数量上可能会有限制,但这取决于所使用的语言
- 虽然已损坏,但 (x)HTML 不会导致严重错误。它没有经过消毒。
无论如何,它只是一个正则表达式模式,但它揭示了开发许多强大实现的可能性。
我编写此模式是为了支持我在框架中构建的模板引擎的递归下降解析器,并且性能非常好,无论是在执行时间还是内存使用方面(与使用相同语法的其他模板引擎无关)。
评论
如果您只想要标签名称,应该可以通过正则表达式来实现。
<([a-zA-Z]+)(?:[^>]*[^/] *)?>
应该做你需要的。但我认为“莫里茨”的解决方案已经很好了。我一开始没有看到它。
对于所有反对者:在某些情况下,使用正则表达式是有意义的,因为它可能是最简单、最快捷的解决方案。我同意,一般来说,您不应该使用正则表达式解析 HTML。
但是,当您有一个 HTML 子集时,正则表达式可能是一个非常强大的工具,您知道格式并且只想提取一些值。我这样做了数百次,几乎总是能实现我想要的。
OP 似乎没有说他需要对标签做什么。例如,他是否需要提取内部文本,或者只是检查标签?
我坚定地认为正则表达式不是万能的、最终的文本解析器。我编写了大量的文本解析代码,包括用于解析 HTML 标记的代码。
虽然我确实不太擅长正则表达式,但我认为正则表达式太僵化了,很难进行这种解析。
有些人会告诉你地球是圆的(或者如果他们想用奇怪的词,地球可能是一个扁球体)。他们在撒谎。
有些人会告诉你,正则表达式不应该是递归的。他们在限制你。他们需要征服你,他们通过让你处于无知状态来做到这一点。
你可以活在他们的现实中,也可以服用红色药丸。
像元帅勋爵(他是元帅.NET类的亲戚吗?)一样,我看过基于Underverse Stack Regex-Verse,并带着你无法想象的力量知识回来了。是的,我认为有一两个老家伙保护他们,但他们在电视上看足球,所以这并不困难。
我认为XML的情况非常简单。正则表达式(在 .NET 语法中)经过缩减并以 base64 编码,以使您虚弱的头脑更容易理解,应该是这样的:
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
+r/vD34mUADO1P4/AQAA//8=
要设置的选项是 。您要查找的捕获组是 。如果捕获组不为空,则存在解析错误,正则表达式停止。RegexOptions.ExplicitCapture
ELEMENTNAME
ERROR
如果您在将其重新转换为人类可读的正则表达式时遇到问题,这应该会有所帮助:
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。
哦。。。如果你想要正则表达式的源代码,使用一些辅助方法:
评论
我喜欢用正则表达式解析 HTML。我不会尝试解析故意破坏的白痴 HTML。这段代码是我的主要解析器(Perl版本):
$_ = join "",<STDIN>; tr/\n\r \t/ /s; s/</\n</g; s/>/>\n/g; s/\n ?\n/\n/g;
s/^ ?\n//s; s/ $//s; print
它被称为 htmlsplit,将 HTML 拆分为行,每行上有一个标签或文本块。然后可以使用其他文本工具和脚本(例如 grep、sed、Perl 等)进一步处理这些行。我什至不是在开玩笑:)享受。
如果你想处理巨大的网页,将我的 slurp-everything-first Perl 脚本重新调整成一个不错的流媒体内容非常简单。但这并不是真的必要。
一些更好的正则表达式:
/(<.*?>|[^<]+)\s*/g # Get tags and text
/(\w+)="(.*?)"/g # Get attibutes
它们对 XML / XHTML 有好处。
通过微小的变化,它可以应对凌乱的 HTML......或先将 HTML -> XHTML 转换为 XHTML。
编写正则表达式的最佳方法是使用 Lex / Yacc 样式,而不是不透明的单行代码或注释的多行怪物。我还没有在这里这样做;这些人几乎不需要它。
评论
/(\w+)="(.*?)"/
/(\w+)="(.*?)"/
<img title="Nope down='up' for aussies" src="..." />
/(\w+)="(.*?)"/
s/>/>\n/g
解决方案如下:
<?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]);
?>
为了深入测试它,我在字符串中输入了自动关闭标签,例如:
- <小时/>
- <br/>
- <br>
我还输入了以下标签:
- 一个属性
- 多个属性
- 值绑定到单引号或双引号中的属性
- 当分隔符为双引号时包含单引号的属性,反之亦然
- “unpretty”属性在“=”符号之前、之后以及之前和之后都有一个空格。
如果您发现上述概念验证中不起作用的内容,我可以分析代码以提高我的技能。
<编辑>我忘记了用户的问题是避免解析自闭合标签。 在这种情况下,模式更简单,变成这样:
$pattern = '/<(\w+)(\s+(\w+)\s*\=\s*(\'|")(.*?)\\4\s*)*\s*>/';
用户@ridgerunner注意到,该模式不允许使用不加引号的属性或没有值的属性。在本例中,微调为我们带来了以下模式:
$pattern = '/<(\w+)(\s+(\w+)(\s*\=\s*(\'|"|)(.*?)\\5\s*)?)*\s*>/';
</编辑>
了解模式
如果有人有兴趣了解更多关于该模式的信息,我提供了一些行:
- 第一个子表达式 (\w+) 与标记名称匹配
- 第二个子表达式包含属性的模式。它由以下部分组成:
- 一个或多个空格 \s+
- 属性的名称 (\w+)
- 零个或多个空格 \s*(可能或不可能,在此处留空)
- “=”符号
- 同样,零个或多个空格
- 属性值的分隔符,单引号或双引号 ('|“)。在模式中,单引号被转义,因为它与 PHP 字符串分隔符重合。这个子表达式是用括号捕获的,因此可以再次引用它来解析属性的闭包,这就是为什么它非常重要。
- 属性的值,几乎与任何内容匹配:(.*?);在这种特定语法中,使用贪婪匹配(星号后面的问号),RegExp 引擎会启用类似“look-ahead”的运算符,该运算符匹配除此子表达式后面的内容之外的任何内容
- 有趣的是:\4 部分是一个反向引用运算符,它指的是之前在模式中定义的子表达式,在本例中,我指的是第四个子表达式,这是找到的第一个属性分隔符
- 零个或多个空格 \s*
- 属性子表达式到此结束,指定零个或多个可能的出现,由星号给出。
- 然后,由于标记可能以“>”符号前的空格结尾,因此零个或多个空格将与 \s* 子模式匹配。
- 要匹配的标记可以以简单的“>”符号结尾,也可以使用前面的斜杠的 XHTML 闭包:(/>|>)。当然,斜杠是转义的,因为它与正则表达式分隔符重合。
小提示:为了更好地分析此代码,有必要查看生成的源代码,因为我没有提供任何 HTML 特殊字符转义。
评论
<option selected>
<p id=10>
< a href="http://wtf.org" >
<namespace:name>
我同意解析 XML 尤其是 HTML 的正确工具是解析器而不是正则表达式引擎。然而,正如其他人所指出的那样,有时使用正则表达式会更快、更容易,并且如果您知道数据格式,就可以完成工作。
Microsoft 实际上有一节是 .NET Framework 中正则表达式的最佳实践,专门讨论了考虑输入源。
正则表达式确实有局限性,但您是否考虑过以下几点?
.NET Framework 在正则表达式方面是独一无二的,因为它支持平衡组定义。
- 请参见将平衡构造与 .NET 正则表达式匹配
- 请参阅 .NET 正则表达式:正则表达式和平衡匹配
- 请参阅 Microsoft 关于平衡组定义的文档
出于这个原因,我相信您可以使用正则表达式解析 XML。但请注意,它必须是有效的 XML(浏览器对 HTML 非常宽容,并且允许在 HTML 中使用错误的 XML 语法)。这是可能的,因为“平衡组定义”将允许正则表达式引擎充当 PDA。
引自上文引述的第1条:
.NET 正则表达式引擎
如上所述,适当平衡的结构不能用 正则表达式。但是,.NET 正则表达式引擎 提供了一些允许平衡构造的构造 认可。
(?<group>)
- 将捕获的结果推送到捕获堆栈上 名称组。(?<-group>)
- 弹出最上面的捕获,名称组关闭 捕获堆栈。(?(group)yes|no)
- 如果存在组,则匹配 yes 部分 否则,名称组不匹配任何部分。这些构造允许 .NET 正则表达式模拟 通过本质上允许堆栈的简单版本来限制 PDA 操作:推、爆和清空。简单的操作几乎 分别等同于递增、递减和比较为零。 这允许 .NET 正则表达式引擎识别 上下文无关语言的子集,特别是那些仅 需要一个简单的计数器。这反过来又允许非传统的 .NET 正则表达式,用于识别单个正确平衡的 构建。
请考虑以下正则表达式:
(?=<ul\s+id="matchMe"\s+type="square"\s*>)
(?>
<!-- .*? --> |
<[^>]*/> |
(?<opentag><(?!/)[^>]*[^/]>) |
(?<-opentag></[^>]*[^/]>) |
[^<>]*
)*
(?(opentag)(?!))
使用以下标志:
- 单线
- IgnorePatternWhitespace(如果折叠正则表达式并删除所有空格,则不需要)
- 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
可以在 A Better .NET Regular Expression Tester 中尝试此操作。
我使用了以下示例源:
<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>
最后,我真的很喜欢 Jeff Atwood 的文章:Parsing Html The Cthulhu Way。有趣的是,它引用了这个问题的答案,目前有超过 4k 的投票。
评论
System.Text
不是 C# 的一部分。它是 .NET 的一部分。
(?=<ul\s*id="matchMe"\s*type="square"\s*>) # match start with <ul id="matchMe"...
\s+
\s*
\s+
\s*
<img src="images/pic.jpg" />
/
<img src="images/pic.jpg" />
<\s*(\w+)[^/>]*>
零件解释:
<
:起始字符
\s*
:标签名称前可能有空格(丑陋,但可能)。
(\w+)
:标签可以包含字母和数字 (H1)。好吧,也匹配“_”,但我想这并没有什么坏处。如果好奇,请改用 ([a-zA-Z0-9]+)。\w
[^/>]*
:除关闭外的任何内容>
/
>
>
:关闭>
不相关的
对于那些低估正则表达式的研究员,他们说它们只和常规语言一样强大:
一个不规则甚至不脱离上下文的n ban 可以匹配^(a+)b\1b\1$
反向引用 FTW!
评论
O(MN)
O(MN)
<a href="foo" title="5>3"> Oops </a>
(引用@Gareth - 奇怪的是,人们如何一遍又一遍地发布具有这种特定缺陷的答案。CDATA 有点容易被忽视,但这更基本)
/
<a href="example.com/test/example.html">
孙子,中国古代战略家、将军和哲学家,说:
据说,如果你了解你的敌人,了解你自己,你可以赢得一百场战斗而不输一场。 如果你只了解自己,而不了解你的对手,你可能会赢,也可能输。 如果你既不了解自己,也不了解你的敌人,你将永远危及自己。
在这种情况下,你的敌人是 HTML,你要么是你自己,要么是正则表达式。你甚至可能是具有不规则正则表达式的 Perl。了解 HTML。了解你自己。
我写了一首俳句,描述了HTML的本质。
HTML has
complexity exceeding
regular language.
我还写了一首俳句,描述了 Perl 中正则表达式的本质。
The regex you seek
is defined within the phrase
<([a-zA-Z]+)(?:[^>]*[^/]*)?>
评论
<([a-zA-Z]+)(?:[^>]*[^/]*)?>
我认为这可能会奏效
<[a-z][^<>]*(?:(?:[^/]\s*)|(?:\s*[^/]))>
这可以在这里进行测试。
根据 W3Schools...
XML 命名规则
XML 元素必须遵循以下命名规则:
- 名称可以包含字母、数字和其他字符
- 名称不能以数字或标点符号开头
- 名称不能以字母 xml(或 XML、XML 等)开头
- 名称不能包含空格
- 可以使用任何名称,并且不保留任何单词。
我使用的模式将遵守这些规则。
评论
>
href="some_other_page.html"
诚然,在编程时,在处理 HTML 时,通常最好使用专用的解析器和 API,而不是正则表达式,尤其是在准确性至关重要的情况下(例如,如果您的处理可能具有安全隐患)。但是,我并不认为 XML 样式的标记永远不应该使用正则表达式进行处理。在某些情况下,正则表达式是完成这项工作的绝佳工具,例如在文本编辑器中进行一次性编辑、修复损坏的 XML 文件或处理看起来像但不完全是 XML 的文件格式时。有一些问题需要注意,但它们并非不可克服,甚至不一定相关。
在我刚才提到的情况下,一个简单的正则表达式通常就足够了。考虑到所有因素,这是一个幼稚的解决方案,但它确实正确地允许在属性值中使用未编码的符号。例如,如果您正在寻找标签,您可以将其改编为 .<([^>"']|"[^"]*"|'[^']*')*>
>
table
</?table\b([^>"']|"[^"]*"|'[^']*')*>
为了给人一种更“高级”的 HTML 正则表达式的感觉,下面在模拟真实世界的浏览器行为和 HTML5 解析算法方面做得相当不错:
</?([A-Za-z][^\s>/]*)(?:=\s*(?:"[^"]*"|'[^']*'|[^\s>]+)|[^>])*(?:>|$)
以下内容与相当严格的 XML 标记定义相匹配(尽管它没有考虑 XML 名称中允许的完整 Unicode 字符集):
<(?:([_:A-Z][-.:\w]*)(?:\s+[_:A-Z][-.:\w]*\s*=\s*(?:"[^"]*"|'[^']*'))*\s*/?|/([_:A-Z][-.:\w]*)\s*)>
当然,这些没有考虑周围的上下文和一些边缘情况,但如果你真的愿意,即使是这样的事情也可以处理(例如,通过在另一个正则表达式的匹配项之间搜索)。
归根结底,使用最适合工作的工具,即使该工具恰好是正则表达式。
如果您只是想查找这些标签(没有解析的野心),请尝试以下正则表达式:
/<[^/]*?>/g
我在 30 秒内写完了它,并在这里进行了测试:https://regexr.com/
它与您提到的标签类型匹配,同时忽略您说要忽略的类型。
评论
下面是 XML/XHTML 的 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/XHTML,共识是否定的!
感谢 nikic 提供这个想法。
正则表达式匹配开放标记,但 XHTML 自包含标记除外 所有其他标记
(和内容)都将被跳过。
这个正则表达式就是这样做的。如果您只需要匹配特定的 Open 标记,请交替列出一个列表
,并在下面的适当位置替换构造
。(?:p|br|<whatever tags you want>)
[\w:]+
<(?:(?:(?:(script|style|object|embed|applet|noframes|noscript|noembed)(?:\s+(?>"[\S\s]*?"|'[\S\s]*?'|(?:(?!/>)[^>])?)+)?\s*>)[\S\s]*?</\1\s*(?=>)(*SKIP)(*FAIL))|(?:[\w:]+\b(?=((?:"[\S\s]*?"|'[\S\s]*?'|[^>]?)*)>)\2(?<!/))|(?:(?:/?[\w:]+\s*/?)|(?:[\w:]+\s+(?:"[\S\s]*?"|'[\S\s]*?'|[^>]?)+\s*/?)|\?[\S\s]*?\?|(?:!(?:(?:DOCTYPE[\S\s]*?)|(?:\[CDATA\[[\S\s]*?\]\])|(?:--[\S\s]*?--)|(?:ATTLIST[\S\s]*?)|(?:ENTITY[\S\s]*?)|(?:ELEMENT[\S\s]*?))))(*SKIP)(*FAIL))>
https://regex101.com/r/uMvJn0/1
# Mix html/xml
# https://regex101.com/r/uMvJn0/1
<
(?:
# Invisible content gets failed
(?:
(?:
# Invisible content; end tag req'd
( # (1 start)
script
| style
| object
| embed
| applet
| noframes
| noscript
| noembed
) # (1 end)
(?:
\s+
(?>
" [\S\s]*? "
| ' [\S\s]*? '
| (?:
(?! /> )
[^>]
)?
)+
)?
\s* >
)
[\S\s]*? </ \1 \s*
(?= > )
(*SKIP)(*FAIL)
)
|
# This is any open html tag we will match
(?:
[\w:]+ \b
(?=
( # (2 start)
(?:
" [\S\s]*? "
| ' [\S\s]*? '
| [^>]?
)*
) # (2 end)
>
)
\2
(?<! / )
)
|
# All other tags get failed
(?:
(?: /? [\w:]+ \s* /? )
| (?:
[\w:]+
\s+
(?:
" [\S\s]*? "
| ' [\S\s]*? '
| [^>]?
)+
\s* /?
)
| \? [\S\s]*? \?
| (?:
!
(?:
(?: DOCTYPE [\S\s]*? )
| (?: \[CDATA\[ [\S\s]*? \]\] )
| (?: -- [\S\s]*? -- )
| (?: ATTLIST [\S\s]*? )
| (?: ENTITY [\S\s]*? )
| (?: ELEMENT [\S\s]*? )
)
)
)
(*SKIP)(*FAIL)
)
>
首先,回答一个直接的问题:你的正则表达式有一个错误,因为它会在任何地方排除带有斜杠的标签,而不仅仅是在末尾。例如,它将排除此有效的开始标记:因为它在属性值中有一个斜杠。<a href="foo/bar.html">
我们可以解决这个问题,但更严重的是,这个正则表达式会导致误报,因为它也会匹配注释和 cdata 部分,其中相同的字符不代表有效的标签。例如:
<!-- <foo> -->
或
<![CDATA[ <foo> ]]>
特别是嵌入在脚本中的 html 字符串可能会触发误报,JavaScript 中经常使用 and 作为比较运算符也是如此。当然还有用 .<
>
<!-- -->
因此,要仅匹配实际标签,您还需要能够跳过过去的注释和 cdata 部分。因此,您需要正则表达式来匹配注释和 cdata,但只捕获开始标记。使用rexep仍然可以做到这一点,但它变得更加复杂,例如:
(
<!-- .*? --> # comment
| <!\[CDATA\[ .*? \]\]> # CData section
| < \w+ ( "" [^""]* "" | ' [^']* ' | [^>/'""] )* /> # self-closing tag
| (?<tag> < \w+ ( "" [^""]* "" | ' [^']* ' | [^>/'""] )* > ) # opening tag - captured
| </ \w+ \s* > # end tag
)
这仅适用于符合 HTML 兼容性准则的 XHTML。如果要处理任意 XHTML,还应该处理处理指令和内部 DTD,因为它们也可以嵌入误报。如果您还想处理 HTML,则还有其他复杂性,例如 -tag。如果你还想处理无效的HTML,它会变得更加复杂。<script>
鉴于复杂性,我不建议走这条路。相反,寻找一个现成的 (X)HTML 解析库,它可以解决您的问题。
解析器通常在后台使用正则表达式(或类似表达式)将文档拆分为“标记”(文档类型、开始标签、结束标签、文本内容等)。但是其他人会为您调试和测试这些正则表达式!根据解析器的类型,它可以通过将开始标记与结束标记匹配来进一步构建元素的树结构。这几乎肯定会为您节省大量时间。
要使用的确切解析器库取决于您的语言和平台以及您正在解决的任务。如果你需要访问实际的标签子字符串(例如,如果你正在为 HTML 编写语法高亮),你需要使用 SAX 风格的解析器,它直接公开语法标记。
如果您只是为了手动构建元素的语法树而执行标记匹配,那么 DOM 解析器会为您完成这项工作。但是 DOM 解析器不会公开底层标签语法,因此不能解决您描述的确切问题。
您还应该考虑是否需要解析无效的 HTML。这是一项更复杂的任务,但在狂野的网络上,大多数 HTML 实际上是无效的。像 Pytons html5lib 这样的东西可以解析无效的 HTML。
<([a-z][^>\s]*)(?:\s+[^>]+)?>
此正则表达式将匹配由单个单词(例如 <p>、<a> 等)组成的开始标记,后跟零个或多个空格以及结束>字符之前的任意数量的字符(>或空格除外)。它还会将标签与属性进行匹配,并且不会将标签与名称中包含 a-z 以外的字符的标签进行匹配。但是,它仍然不匹配自闭合标记。
若要匹配除 XHTML 自包含标记之外的打开标记(开始标记),可以使用以下正则表达式:
<[^/][^>]*>
<
:匹配左尖括号。[^/]
:匹配除正斜杠以外的任何字符,确保标签不是结束标签。/
[^>]*
:匹配零个或多个字符,而不是右尖括号,允许存在任何属性。>
>
:匹配右尖括号,完成标记。
评论
<tag/>
<
[^/]
t
[^>]*
ag/
>
<[^\/>]+>
/
>
评论