您能否举例说明为什么难以用正则表达式解析XML和HTML?

时间:2009-03-31 14:13:40

标签: html xml regex

我发现人们制作overover again的一个错误是尝试使用正则表达式解析XML或HTML。以下是解析XML和HTML很难的几个原因:

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

<tag
attr="5"
/>

人们想要治疗&lt;或者&lt; 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>

你还知道其他什么问题?

12 个答案:

答案 0 :(得分:258)

这里有一些有趣的有效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>

更不用说针对无效构造的所有特定于浏览器的解析。

祝好运对抗那个正则表达式!

EDIT(JörgWMittag):这是另一个结构良好,有效的HTML 4.01:

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

答案 1 :(得分:67)

实际上

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

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

它不是有效的XML,因为'&lt;'和'&gt;'属性字符串中的有效字符不是。它们需要使用相应的XML实体进行转义&amp; lt;和&amp; 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。标签的名称必须在'&lt;'后面,尽管属性和结束'&gt;'可能在他们想要的任何地方。所以有效的XML实际上是

<tag
attr="5"
/>

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

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

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

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

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

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

HTML甚至难以自己解析。首先,法律语法有许多你可能不知道的微妙之处,其次,野外的HTML只是一堆巨大的(你得到我的漂移)。有很多松散的解析器库可以很好地处理像标签汤这样的HTML,只需使用它们。

答案 2 :(得分:55)

我写了一篇关于这个主题的完整博客文章:Regular Expression Limitations

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

前一段有一点需要注意。某些正则表达式实现现在支持递归的想法。但是,一旦你开始在你的正则表达式中添加递归,你实际上是在扩展边界,应该考虑一个解析器。

答案 3 :(得分:20)

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

答案 4 :(得分:16)

这取决于“解析”的含义。一般来说,XML无法使用正则表达式进行解析,因为XML语法绝不是常规的。简而言之,正则表达式无法计算(好吧,Perl正则表达式实际上可以计算内容),因此您无法平衡开闭标记。

答案 5 :(得分:9)

人们是否真的通过使用正则表达式犯了错误,或者它是否足以完成他们试图实现的任务?

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

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

答案 6 :(得分:6)

人们通常默认写贪婪的模式,往往足以导致无法思考。*将大块文件啜饮到尽可能大的&lt; foo&gt;。*&lt; / foo&gt ;.

答案 7 :(得分:6)

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

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

因为解析XML是 hard 。通过不必学习使用XML解析库而节省的任何努力将超过您必须做的创造性工作量和错误捕获量。为了您自己,谷歌“XML库”并利用其他人的工作。

答案 8 :(得分:4)

我相信this 经典包含您要查找的信息。您可以在其中一条评论中找到要点:

  

我认为这里的缺陷是HTML是Chomsky Type 2语法   (无上下文语法)和RegEx是Chomsky Type 3语法(常规   表达)。 因为Type 2语法基本上比复杂   类型3语法 - 你不可能希望这项工作。但很多   会尝试,有些人会说成功,有些人会找到错误   完全搞砸了你。

来自维基百科的更多信息:Chomsky Hierarchy

答案 9 :(得分:4)

我认为问题归结为:

  1. 正则表达式几乎总是不正确的。有合法的输入,它将无法正确匹配。如果你努力工作,你可以使它达到99%正确,或99.999%,但如果只是因为XML允许使用实体的奇怪事情,那么使其100%正确是不可能的。

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

  3. 如果正则表达式足以覆盖99.99%的案例,那么它将完全不可读且无法维护。

  4. 正则表达式很可能会对中等大小的输入文件执行得非常糟糕。我第一次遇到XML就是用一个合适的XML解析器替换一个(错误地)解析传入XML文档的Perl脚本,我们不仅用100行代替300行不可读代码,而且我们改进了用户响应时间从10秒到大约0.1秒。

答案 10 :(得分:1)

  

一般来说,使用正则表达式无法解析XML,因为XML语法绝不是常规的。简而言之,正则表达式无法计算(好吧,Perl正则表达式实际上可以计算内容),因此您无法平衡开闭标记。

我不同意。如果你在regex中使用recursive,你可以很容易地找到打开和关闭的标签。

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

答案 11 :(得分:1)

我简要回答了这个问题here。虽然它没有占到100%的标记,但如果您愿意做一些预处理工作,我会解释它是如何可能的。