您所在的位置: 首页>>读书频道>>设计开发>>其它开发>>

表达式主导与文本主导

http://book.51cto.com  2008-01-08 14:46  Friedl,J.E.F.著/余晟译  电子工业出版社博文视点  我要评论(0)

表达式主导与文本主导
Regex-Directed Versus Text-Directed

DFA和NFA反映了将正则表达式在应用算法上的根本差异。我把对应汽油机的NFA称为“表达式主导(regex-directed)”引擎,而对应电动机的DFA称为“文本主导(text-directed)”引擎。

NFA引擎:表达式主导
NFA Engine: Regex-Directed

我们来看用「to(nite|knight|night)」匹配文本‘…tonight…’的一种办法。正则表达式从「t」开始,每次检查一部分(由引擎查看表达式的一部分),同时检查“当前文本(current text)”是否匹配表达式的当前部分。如果是,则继续表达式的下一部分,如此继续,直到表达式的所有部分都能匹配,即整个表达式能够匹配成功。

测验答案

153页测验的答案
用「^.*([0-9]+)」匹配‘copyright 2003.’,括号会捕获到什么?

这个表达式的本意是捕获整个数字2003,但结果并非如此。之前已经说过,为了满足「[0-9]+」的匹配,「.*」必须交还一些字符。在这个例子中,释放的字符是最后的‘3’和点号,之后‘3’能够由「[0-9]」 匹配。「[0-9]」由「+」量词修饰,所以现在还只做到了最小的匹配可能,现在它遇到了‘.’,找不到其他可以匹配的字符。
与之前不同,此时没有“必须”匹配的元素,所以「.*」不会被迫交出0。否则,「[0-9]+」应当心存感激,接受匹配优先元素的馈赠,但请记住“先来先服务”原则。匹配优先的结构只会在被迫的情况下交还字符。所以,最终$1的值是‘3’。

如果读者觉得难以理解,不妨这样想,「[0-9]+」和「[0-9]*」差不多,而本例中「[0-9]*」和「.*」是一样的。用它来替换原来的表达式「^.*([0-9]+)」,我们得到「^.*(.*)」,这与152页的「^Subject:(.*).*」很相似,第二个「.*」不会匹配任何字符。

在「to(nite|knight|night)」的例子中,第一个元素是「t」,它将会重复尝试,直到在目标字符串中找到‘t’为止。之后,就检查紧随其后的字符是否能由「o」匹配,如果能,就检查下面的元素。在本例中,“下面的元素”指「(nite|knight|night)」它的真正含义是“「nite」或者「knight」或者「night」”。引擎会依次尝试这3种可能。我们(具有高级神经网络的人类)能够发现,如果待匹配的字符串是tonight,第三个选择能够匹配。不论神经学起源(85)如何,表达式主导的引擎必须完全测试,才能得出结论。

尝试「nite」的过程与之前一样:“尝试匹配「n」,然后是「i」,然后是「t」,最后是「e」。”如果这种尝试失败——就像本例,引擎会尝试另一种可能,如此继续下去,直到匹配成功或是报告失败。表达式中的控制权在不同的元素之间转换,所以我称它为“表达式主导”。

NFA引擎在操作上的优点

实质上,在表达式主导的匹配过程中,每一个子表达式都是独立的。这不同于反向引用,子表达式之间不存在内在联系,而只是整个正则表达式的各个部分。在子表达式与正则表达式的控制结构(多选分支、括号以及匹配量词)的层级关系(layout)控制了整个匹配过程。
因为NFA引擎是正则表达式主导的,驾驶员(也就是编写表达式的人)有充足的机会来实现他 / 她期望的结果(第5章和第6章将会告诉读者,如何正确高效地实现目标)。现在看起来,这点还有些模糊,但过一段就会变清晰。

DFA引擎:文本主导
DFA Engine: Text-Directed

与表达式主导的NFA不同,DFA引擎在扫描字符串时,会记录“当前有效(currently in the works)”的所有匹配可能。具体到tonight的例子,引擎移动到t时,它会在当前处理的匹配可能中添加一个潜在的可能:

字符串中的位置

正则表达式中的位置

aftertæonight

可能的匹配位置:tæo(nite|knight|night)


接下来扫描的每个字符,都会更新当前的可能匹配序列。继续扫描两个字符以后的情况是:

字符串中的位置

正则表达式中的位置

aftertoniæght

可能的匹配位置:to(niæte|knight|niæght)


有效的可能匹配变为两个(knight被淘汰出局)。扫描到g时,就只剩下一个可能匹配了。当h和t匹配完成后,引擎发现匹配已经完成,报告成功。
 
我称这种方式为“文本主导”,是因为它扫描的字符串中的每个字符都对引擎进行了控制。在本例中,某个未完成的匹配也许是任意多个(只要可行)匹配的开始。不合适的匹配可能在扫描后继文字时会被去除。在某些情况下,“处理中的未终结匹配(partial match in progress)”可能就是一个完整的匹配。例如正则表达式「to(…)?」,括号内的部分并不是必须出现的,但考虑到匹配优先的性质,引擎仍然会尝试匹配括号内的部分。匹配过程中,在尝试括号内的部分时,完整匹配(‘to’)已经保留下来,以应付括号中的内容无法匹配的情况。

如果引擎发现,文本中出现的某个字符会令所有处理中的匹配可能失效,就会返回某个之前保留的完整匹配。如果不存在这样的完整匹配,则要报告在当前位置无法匹配。

【责任编辑:杜书 TEL:(010)68476606】

回书目   上一节   下一节
深入Vista应用程序开发
Ruby on Rails 社区网站开发
ASP.NET从入门到精通
Java完全自学宝典
JavaScript完全自学宝典
 
 验证码: (点击刷新验证码)   匿名发表
  • Visual C++ 完全自学宝典

  • 作者:强锋科技,朱洪波
  • Visual C++ 6.0是微软公司为程序人员提供的Visual Studio 6.0工具套件中的重要组成部分。本书由浅入深地介绍使用Visual C++ 6.0..
Copyright©2005-2008 51CTO.COM 版权所有