您能否提供一些示例,说明为什么用正则表达式很难解析XML和HTML?[关闭]


402

一个错误我看到人们做,并再次试图解析XML或HTML用正则表达式。以下是难以解析XML和HTML的一些原因:

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

<tag
attr="5"
/>

人们希望将<或<标记视为标记的开头,但是这种情况在野外存在:

<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>

您还知道其他哪些陷阱?


14
Web浏览器每秒可以处理数百万次此类混乱,有人不能为我们这些凡人创建网页解析器类吗?
乔恩·温斯坦利

24
乔恩,他们有。在Perl中,有许多HTML :: Parser,HTML :: TreeBuilder等。几乎可以肯定的是您的语言之一。
Chas。欧文斯2009年

12
最好的答案是,stackoverflow.com /a/1732454/135078(请注意Zalgo)
Kelly S. French

3
有一个很好的解释,为什么[您无法使用正则表达式解析[X] HTML] [1] [1]:stackoverflow.com/a/1732454/468725
Pavel P

4
这里是如何你一个很好的解释当然可以解析HTML与模式,以及为什么你可能不希望这样做。
tchrist

Answers:


260

这是一些有趣的有效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örgW 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/>

6
XML之一?那里有几种不同的构造,这麻烦吗?DTD内部子集?这定义了一个新的&entity; 称为“ y”,包含一个“]>”序列,该序列通常会在内部子集结尾(如果不带引号)。
bobince

16
(这表明即使您不是DTD验证解析器,您也必须具有XML的一些更深奥和古老的DTD功能的相当深的知识才能正确地解析文档。)
bobince

17
HTML示例利用了一个鲜为人知的功能:短标签。在w3.org/QA/2007/10/shorttags.html上
netvope 2010年

25
每当有人如上所述编写HTML时,Tim Berners-Lee都会流下眼泪。
fgysin恢复莫妮卡

5
我喜欢Stackoverflow的“语法突出显示”如何在第一次出现“]”时失败。
GlassGhost 2012年

71

其实

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

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

这不是有效的XML,因为'<'和'>'在属性字符串中不是有效的字符。需要使用相应的XML实体&lt; 和&gt;

它也不是有效的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的最大问题是人们通常无法正确理解所有语法规则。您的浏览器将您的tagoup解释为HTML的事实并不意味着您实际上已经编写了有效的HTML。

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

基本上,XML不能使用正则表达式进行解析。但是也没有理由这样做。每种语言都有很多许多XML解析器。您可以在SAX解析器,DOM解析器和Pull解析器之间进行选择。所有这些保证都比使用正则表达式解析要快得多,然后您可以在结果DOM树上使用诸如XPath或XSLT之类的出色技术。

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

HTML太难了,甚至无法尝试自己解析。首先,法律语法具有许多您可能不知道的细微之处,其次,狂野的HTML只是一大堆令人讨厌的东西(您不知所措)。有各种各样的松散解析器库可以很好地处理诸如标记汤之类的HTML,只需使用它们即可。


8
您不必转义> as>。
乔伊(Joey)

8
好的,s /有效/存在于野外/ g
Chas。欧文斯2009年

1
实际上,根据规范,您必须转义> as>,就像您必须转义<as <&and&amp; 并在属性中将“ as”和“ as”表示为许多解析器
LordOfThePigs

19
规范中没有说'>'必须转义-内容中序列']]>'的特殊情况除外。因此,最容易总是转义'>',但这不是规范所必需的。
bobince

8
>标志是完全有效的HTML stackoverflow.com/questions/94528/...
JFS

56

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

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

上一段带有一些警告。现在,某些正则表达式实现支持递归的想法。但是,一旦开始将递归添加到正则表达式中,您实际上就在扩展边界,应该考虑使用解析器。


20

不在列表中的一个陷阱是属性可以按任何顺序出现,因此,如果您的正则表达式正在查找带有href“ foo”和类“ bar”的链接,则它们可以按任意顺序出现,并且可以具有任意其他数量他们之间的事情。


嗯,是的,这甚至是促使我问这个问题的问题(第一个链接)。
Chas。欧文斯

16

这取决于您“解析”的意思。一般来说,由于XML语法绝非常规,因此无法使用正则表达式来解析XML。简而言之,正则表达式无法计数(嗯,Perl正则表达式实际上可以计数),因此您无法平衡开闭标签。


我猜反向引用可以解决打开和关闭标签的问题
Rishul Matta 2014年

1
@RishulMatta:怎么样?您只有数量有限的反向引用,并请注意您需要反转标记...此外,对正则表达式的严格定义不允许反向引用。
Willem Van Onsem 2015年

.NET允许平衡会弹出和推送的表达式,并且理论上可以用于匹配层次结构。但这仍然不是一个好主意。
亚伯

9

人们实际上是使用正则表达式犯了一个错误,还是仅仅足以胜任他们要完成的任务?

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

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


7
定义“足够好”。不可避免的是,简单的正则表达式将无法正常工作。是不匹配某些东西还是不应该匹配一个不匹配的东西?如果是这样,那么使用正则表达式是一个错误。HTML和XML解析器并不难使用。避免学习它们是错误的经济。
Chas。欧文斯(Owens)2009年

1
好,定义“足够好”。可以说我有一个网页告诉我客户的IP地址。这就是全部。现在,我需要为客户端计算机编写一个应用程序,告诉我其IP地址。我去那个站点,寻找一个IP地址并返回它。不需要解析HTML!
罗宾·

2
如果您有一个完全由您控制格式的任意字符串,那么该字符串恰好是格式正确的XML的事实实际上是无关紧要的。但是,几乎没有XML的用例实际上属于此类。
罗伯特·罗斯尼

15
我可以从痛苦的经历中告诉您,大多数时候都可以使用荒谬的复杂正则表达式模式来获得想要的东西。直到网站经历了一次有趣的小变化,您才能抛出此正则表达式,这让您在窗口外哭了两天,然后重新开始。
Thomasz

@Robert:“几乎没有用例”是一种夸张。以我的经验,有足够的用例。YAGNI在这里适用...有时。诀窍是要知道您要解决的特定任务的解决方案需要多么防弹和长寿。罗宾有一个很好的观点。他只是说完整的XML解析并不总是值得的……即使您知道如何使用它,也是如此。
LarsH 2011年

6

人们通常会默认编写贪婪模式,这通常会导致不加思索的。*将大块文件拖到最大的<foo>。* </ foo>中。


2
除了使重复变得懒惰外.*?<,您可以通过使用否定的字符类来解决这个问题[^<]*<。(免责声明:显然,这仍然不是万无一失的,这是问题的重点。)
罗里·奥肯

6

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

也许正确的陈词滥调始于“当您只有锤子...”时,您知道如何使用正则表达式,正则表达式擅长解析,那么为什么还要花时间学习XML解析库呢?

因为解析XML很困难。您不必学习使用XML解析库而节省的任何工作,将远远超过您必须要做的创造性工作和减少错误的工作。为了您自己的利益,谷歌“ XML库”并利用他人的工作。


3
它并不像C ++那样复杂。
科尔·约翰逊

6
@Cole“ Cole9” Johnson我也不会使用RE来解析C ++。
艾萨克·拉比诺维奇

2
如果XML是同步加速器,则C ++将是大型强子对撞机。
凯文·科斯特兰

4

我相信这本 经典的书可以为您提供所需的信息。您可以在其中的评论之一中找到要点:

我认为这里的缺陷是HTML是Chomsky Type 2语法(无上下文语法),而RegEx是Chomsky Type 3语法(正则表达式)。由于类型2语法从本质上比类型3语法复杂-您不可能希望做到这一点。但是很多人会尝试,有些人会宣称成功,而另一些人会发现错误并完全把你弄糟。

来自Wikipedia的更多信息:Chomsky Hierarchy


6
在形式语法讨论中,“正则表达式”的含义与此处不完全相同。大多数现存的正则表达式引擎比Chomsky Type 3语法更强大(例如,非贪婪匹配,后向引用)。一些正则表达式引擎(例如Perl)已经完成了Turing。的确,即使是那些解析HTML的工具也很差,但是这种经常被引用的观点并不是原因。
dubiousjim

4

我认为问题可以归结为:

  1. 正则表达式几乎总是错误的。存在合法输入,它将无法正确匹配。如果您足够努力,则可以使它达到99%正确或99.999%,但是几乎不可能使它达到100%正确,这仅仅是因为XML通过使用实体允许的怪异事物。

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

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

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


1

一般来说,由于XML语法绝非常规,因此无法使用正则表达式来解析XML。简而言之,正则表达式无法计数(嗯,Perl正则表达式实际上可以计数),因此您无法平衡开闭标签。

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

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


首先,递归正则表达式不是正则表达式(如果您看一下括号,您会发现我承认,Perl的递归正则表达式可以计算处理HTML所需的内容)。其次,您的示例适用于格式良好的XHTML或XML。HTML格式不正确。第三,您必须问自己,是否更容易扩展和维护以递归正则表达式语言或通用编程语言编写的解析器。
Chas。Owens 2015年

第四,即使您的示例在仍然是有效的XML的情况下也被琐碎破坏了。在content_block和id之间添加一个空格,它将失败。我敢肯定,如果我再花几分钟的时间,我会在您的代码中发现其他一些结构错误。这不是一个好主意。
Chas。Owens 2015年

1

在这里对这个问题给出了简化的答案。虽然它不占100%的标记,但我解释了如果您愿意进行一些预处理工作是如何可能的。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.