您能否举例说明为什么很难使用正则表达式解析 XML 和 HTML?[已结束]

Can you provide some examples of why it is hard to parse XML and HTML with a regex? [closed]

提问人:Chas. Owens 提问时间:3/31/2009 最后编辑:CommunityChas. Owens 更新时间:7/21/2020 访问量:51200

问:


想改进这个问题吗?通过编辑这篇文章来更新问题,使其仅关注一个问题。

7年前关闭。

我看到人们一遍又一遍地犯的一个错误是试图用正则表达式解析XML或HTML。以下是解析 XML 和 HTML 困难的几个原因:

人们希望将文件视为一系列行,但这是有效的:

<tag
attr="5"
/>

人们希望将 < 或 <tag 视为标签的开头,但这样的东西存在于野外:

<img src="imgtag.gif" alt="<img>" />

人们通常希望将起始标记与结束标记匹配,但 XML 和 HTML 允许标记包含自身(传统的正则表达式根本无法处理):

<span id="outer"><span id="inner">foo</span></span> 

人们通常希望与文档的内容进行匹配(例如著名的“查找给定页面上的所有电话号码”问题),但数据可能会被标记(即使查看时看起来很正常):

<span class="phonenum">(<span class="area code">703</span>)
<span class="prefix">348</span>-<span class="linenum">3020</span></span>

评论可能包含格式不良或不完整的标签:

<a href="foo">foo</a>
<!-- FIXME:
    <a href="
-->
<a href="bar">bar</a>

您还知道哪些其他问题?

html xml 正则表达式

评论

16赞 Jon Winstanley 3/31/2009
Web 浏览器每秒数百万次就能理解这种混乱,难道有人不能为我们这些凡人创建一个网页解析器类吗?
25赞 Chas. Owens 3/31/2009
乔恩,他们有。在Perl中,有许多HTML::P arser,HTML::TreeBuilder等。几乎可以肯定,您的语言有一个。
0赞 Brian Campbell 4/1/2009
Jon,你在找什么语言,你是在寻找解析格式正确的XML或HTML标签汤吗?
13赞 Kelly S. French 1/13/2012
最好的答案是,stackoverflow.com/a/1732454/135078(当心 Zalgo)
4赞 tchrist 6/7/2014
这里有一个很好的解释,说明你当然可以用模式解析HTML,以及为什么你可能不希望这样做。

答:

17赞 Anton Gogolev 3/31/2009 #1

这取决于你所说的“解析”是什么意思。一般来说,XML 不能使用正则表达式来解析,因为 XML 语法绝不是正则表达式的。简单地说,正则表达式不能计数(好吧,Perl正则表达式实际上可能能够计数),所以你无法平衡打开-关闭标签。

评论

0赞 Rishul Matta 11/3/2014
我想反向引用可以解决打开和关闭标签的问题
1赞 willeM_ Van Onsem 6/2/2015
@RishulMatta:怎么样?您只有有限数量的反向引用,请注意,您需要反转标签...此外,正则表达式的严格定义不允许反向引用。
0赞 Abel 11/18/2018
.NET 允许平衡表达式,这些表达式可以弹出和推送,理论上可用于匹配层次结构。但这仍然是一个坏主意。
62赞 JaredPar 3/31/2009 #2

我写了一篇关于这个主题的博客文章:正则表达式限制

问题的关键在于 HTML 和 XML 是递归结构,需要计数机制才能正确解析。真正的正则表达式无法计数。您必须具有上下文无关的语法才能计数。

上一段有一个轻微的警告。某些正则表达式实现现在支持递归的概念。然而,一旦你开始在你的正则表达式中添加递归,你就真的在扩展边界,应该考虑一个解析器。

6赞 chaos 3/31/2009 #3

人们通常默认编写贪婪的模式,这通常会导致未经深思熟虑的 .* 将大块文件啜饮到尽可能大的 <foo>.*</foo>。

评论

2赞 Rory O'Kane 5/14/2013
除了使重复变得懒惰之外,您还可以通过使用否定的字符类(如 )来解决这个问题。(免责声明:显然这仍然不是万无一失的,这就是问题的重点。.*?<[^<]*<
73赞 LordOfThePigs 3/31/2009 #4

实际上

<img src="imgtag.gif" alt="<img>" />

不是有效的 HTML,也不是有效的 XML。

它不是有效的 XML,因为“<”和“>”不是属性字符串中的有效字符。需要使用相应的 XML 实体对它们进行转义 <和 >

它也不是有效的 HTML,因为 HTML 中不允许使用简短的结束形式(但在 XML 和 XHTML 中是正确的)。根据 HTML 4.01 规范,“img”标签也是一个隐式闭合标签。这意味着手动关闭它实际上是错误的,相当于关闭任何其他标签两次。

HTML 中的正确版本是

<img src="imgtag.gif" alt="&lt;img&gt;">

XHTML 和 XML 中的正确版本是

<img src="imgtag.gif" alt="&lt;img&gt;"/>

您给出的以下示例也是无效的

<
tag
attr="5"
/>

这也不是有效的 HTML 或 XML。标签的名称必须位于“<”的正后方,尽管属性和结束“>”可以位于它们想要的任何位置。所以有效的 XML 实际上是

<tag
attr="5"
/>

这是另一个更有趣的问题:您实际上可以选择使用“或”作为属性引用字符

<img src="image.gif" alt='This is single quoted AND valid!'>

发布的所有其他原因都是正确的,但解析 HTML 的最大问题是人们通常无法正确理解所有语法规则。您的浏览器将您的 tagsoup 解释为 HTML 并不意味着您实际上已经编写了有效的 HTML。

编辑:甚至 stackoverflow.com 也同意我关于有效和无效的定义。您无效的 XML/HTML 没有突出显示,而我的更正版本会突出显示。

基本上,XML 不是用正则表达式解析的。但也没有理由这样做。每种语言都有许多 XML 解析器。您可以在 SAX 解析器、DOM 解析器和拉取解析器之间进行选择。所有这些都保证比使用正则表达式解析快得多,然后您可以在生成的 DOM 树上使用 XPath 或 XSLT 等很酷的技术。

因此,我的回答是:使用正则表达式解析XML不仅很困难,而且也是一个坏主意。只需使用数百万个现有 XML 解析器之一,即可利用 XML 的所有高级功能。

HTML 太难了,甚至无法尝试自己解析。首先,法律语法有许多你可能不知道的小微妙之处,其次,野外的 HTML 只是一大堆臭气熏天的东西(你明白我的漂移)。有各种各样的松散解析器库在处理 HTML 方面做得很好,比如标签汤,只需使用这些。

评论

9赞 Joey 3/31/2009
不过,您不需要像>那样逃避>。
9赞 Chas. Owens 3/31/2009
好的,s/valid/存在于野外/g
1赞 LordOfThePigs 3/31/2009
实际上,根据规范,您必须>>,就像您必须< < & 和 &和属性 “ 作为 ”和 ' as '只是那么多解析器
21赞 bobince 4/1/2009
规范没有说“>”必须转义,但内容中序列“]]>”的特殊情况除外。出于这个原因,最容易始终转义“>”,但规范并不要求这样做。
10赞 jfs 11/28/2009
>符号在 HTML stackoverflow.com/questions/94528/ 中是完全有效的......
10赞 Robin Day 3/31/2009 #5

人们是否真的通过使用正则表达式犯了错误,或者它对于他们试图实现的任务来说已经足够好了?

我完全同意使用正则表达式解析 html 和 xml 是不可能的,因为其他人已经回答了。

但是,如果您的要求不是解析html / xml,而只是在html / xml的“已知良好”位中获取一小部分数据,那么也许正则表达式甚至更简单的“子字符串”就足够了。

评论

8赞 Chas. Owens 3/31/2009
定义“足够好”。不可避免的是,简单的正则表达式是行不通的。不匹配某物或不匹配某物不应该是错误吗?如果是这样,那么使用正则表达式是错误的。HTML 和 XML 解析器并不难使用。避免学习它们是一种虚假的经济。
1赞 Robin Day 4/1/2009
好的,定义“足够好”。假设我有一个网页,告诉我客户端的 IP 地址。这就是它所做的一切。现在,我需要为客户端计算机编写一个应用程序,告诉我它的 IP 地址。我去那个网站,寻找一个IP地址并返回它。不需要解析 HTML!
2赞 Robert Rossney 4/1/2009
如果你有一个格式完全由你控制的任意字符串,那么这个字符串恰好是格式正确的 XML 这一事实实际上并不重要。但实际上,几乎没有任何 XML 用例属于这一类。
17赞 Thomasz 4/4/2009
我可以从痛苦的经历中告诉你,大多数时候,利用荒谬的复杂正则表达式模式可以得到你想要的东西。直到网站经历了一个搞笑的小变化,你可以把这个让你哭了两天的正则表达式扔出窗外,重新开始。
0赞 LarsH 7/19/2011
@Robert:“几乎没有用例”是夸大其词。根据我的经验,有一些足够常见的用例。YAGNI 在这里适用...有时。诀窍是了解您的解决方案需要多么防弹和持久,才能解决您正在处理的特定任务。罗宾说得很有道理。他只是说完整的XML解析并不总是值得的......即使您知道如何使用它,也是如此。
270赞 bobince 4/1/2009 #6

这里有一些有趣的有效 XML:

<!DOCTYPE x [ <!ENTITY y "a]>b"> ]>
<x>
    <a b="&y;>" />
    <![CDATA[[a>b <a>b <a]]>
    <?x <a> <!-- <b> ?> c --> d
</x>

这个小小的喜悦是有效的 HTML:

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd" [
    <!ENTITY % e "href='hello'">
    <!ENTITY e "<a %e;>">
]>
    <title>x</TITLE>
</head>
    <p id  =  a:b center>
    <span / hello </span>
    &amp<br left>
    <!---- >t<!---> < -->
    &e link </a>
</body>

更不用说所有特定于浏览器的无效结构解析了。

祝你好运,让正则表达式与之抗衡!

编辑 (Jörg W Mittag):这是另一个格式良好、有效的 HTML 4.01 的好文章:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
  "http://www.w3.org/TR/html4/strict.dtd"> 
<HTML/
  <HEAD/
    <TITLE/>/
    <P/>

评论

7赞 bobince 4/1/2009
XML的?那里有几种不同的结构,这很麻烦?DTD 内部子集?这是定义一个新的 &实体;称为“y”,包含一个“]>”序列,如果不用引号括起来,通常会结束内部子集。
17赞 bobince 4/1/2009
(这表明,即使您不是 DTD 验证解析器,您也必须对 XML 的一些更深奥和过时的 DTD 功能有相当深入的了解才能正确解析文档。
18赞 netvope 12/15/2010
HTML 示例使用了一个鲜为人知的功能:短标签。更多信息,请访问 w3.org/QA/2007/10/shorttags.html
29赞 fgysin 9/15/2011
每当有人编写如上所示的 HTML 时,Tim Berners-Lee 都会流下一滴眼泪。
6赞 GlassGhost 3/3/2012
我喜欢 Stackoverflow 的语法高亮器在第一次出现“]”时失败。
22赞 AmbroseChapel 4/1/2009 #7

一个不在你列表中的问题是属性可以以任何顺序出现,所以如果你的正则表达式正在寻找带有 href “foo” 和类 “bar” 的链接,它们可以以任何顺序出现,并且在它们之间有任意数量的其他东西。

评论

0赞 Chas. Owens 4/1/2009
啊,是的,这甚至是促使我问这个问题的问题(第一个链接)。
5赞 Adam Arold 1/13/2012 #8

我相信这部经典有你要找的信息。您可以在那里的一条评论中找到要点:

我认为这里的缺陷是 HTML 是 Chomsky Type 2 语法 (上下文无关语法)和 RegEx 是 Chomsky Type 3 语法(常规 表达式)。由于类型 2 语法从根本上比 a 类型 3 语法 - 你不可能指望让它工作。但许多 会尝试,有些人会声称成功,有些人会发现错误和 完全把你搞砸了。

来自维基百科的更多信息:乔姆斯基层次结构

评论

7赞 dubiousjim 5/31/2012
“正则表达式”在正式语法讨论中的含义与这里并不完全相同。大多数现存的正则表达式引擎都比 Chomsky Type 3 语法更强大(例如非贪婪匹配、backrefs)。一些正则表达式引擎(如Perl的)是图灵完备的。诚然,即使是这些也是解析 HTML 的糟糕工具,但这个经常被引用的论点并不是原因。
7赞 Isaac Rabinovitch 7/15/2012 #9

我很想说“不要重新发明轮子”。除了XML是一种非常非常复杂的格式。所以也许我应该说“不要重新发明同步加速器”。

也许正确的陈词滥调是“当你拥有的只是一把锤子......”你知道如何使用正则表达式,正则表达式是擅长解析的,那么为什么还要费心去学习XML解析库呢?

因为解析 XML 很困难。您不必学习使用XML解析库所节省的任何精力都将超过您必须做的创造性工作和错误处理量。为了你自己,谷歌“XML库”并利用别人的工作。

评论

3赞 Cole Tobin 8/13/2013
不过,它并不像C++那么复杂。
8赞 Isaac Rabinovitch 8/13/2013
@Cole“Cole9”Johnson:我也不会使用 RE 来解析C++。
4赞 Kevin Kostlan 9/2/2015
如果XML是同步加速器,那么C++就是大型强子对撞机。
0赞 Maxim Suslov 2/7/2015 #10

一般来说,XML 不能使用正则表达式来解析,因为 XML 语法绝不是正则表达式的。简单地说,正则表达式不能计数(好吧,Perl正则表达式实际上可能能够计数),所以你无法平衡打开-关闭标签。

我不同意。如果您将在正则表达式中使用递归,则可以轻松找到打开和关闭标签。

这里我展示了正则表达式的示例,以避免在第一条消息中解析示例的错误。

评论

1赞 Chas. Owens 2/9/2015
首先,递归正则表达式不是正则表达式(如果你看一下括号,你会看到我承认Perl的正则表达式是递归的,可以计算事物,这是处理HTML所必需的)。其次,您的示例适用于格式正确的 XHTML 或 XML。HTML 格式不正确。第三,你必须问问自己,扩展和维护一个用递归正则表达式语言或通用编程语言编写的解析器是否更容易?
1赞 Chas. Owens 2/9/2015
第四,即使你的示例在仍然是有效的 XML 时也被轻而易举地破坏了。在 content_block 和 id 之间添加一个空格,它会失败。我敢肯定,如果我再花几分钟,我会在您的代码中发现其他一些结构错误。这不是一个好主意。
6赞 Michael Kay 10/22/2015 #11

我认为问题可以归结为:

  1. 正则表达式几乎总是不正确的。有一些合法的输入,它将无法正确匹配。如果你足够努力,你可以使它正确率为99%,或99.999%,但使它100%正确几乎是不可能的,如果仅仅是因为XML通过使用实体允许的奇怪事情。

  2. 如果正则表达式不正确,即使对于 0.00001% 的输入,也存在安全问题,因为有人可能会发现一个会破坏您的应用程序的输入。

  3. 如果正则表达式足够正确,可以涵盖 99.99% 的情况,那么它将完全不可读且无法维护。

  4. 正则表达式很可能在中等大小的输入文件上表现得非常糟糕。我第一次接触 XML 是用适当的 XML 解析器替换(错误地)解析传入的 XML 文档的 Perl 脚本,我们不仅用任何人都可以理解的 100 行替换了 300 行不可读的代码,而且我们将用户响应时间从 10 秒缩短到大约 0.1 秒。

1赞 Erutan409 11/22/2015 #12

我在这里给出了这个问题的简化答案。虽然它没有达到 100% 的分数,但我解释了如果你愿意做一些预处理工作,这怎么可能。