VBA正则表达式入门与提高 下载本文

正则表达式入门与提高

配“p”,失败了。于是引擎重新从位置4开始,即引擎再继续从第五个字符重新检查匹配性。直到第十五个字符开始,<>匹配上了“catfish”中的“cat”.至此,引擎报告成功.引擎停在这个位置.

这时,如果global属性为False,那么,整个匹配结束.反之,引擎从该结束位置(即catfish后的位置上)开始,重复上述过程,继续前行,直至目标文本的最后一个位置.

结论:正则表达式”cat”,并不是匹配”单词cat”.它的本质意义是首先匹配一个字符”c”,紧接着一个字符”a”,再接着一个字符”t”的这样一个字符串.(你能用前面所学知识,只匹配目标文本的最后单词”cat”吗?)

2. 引擎依次在目标文本的每一个位置上,尝试整个正则表达式中的所有子表达和组成元素,直到匹配失败,才移动到下一个位置.

在目标文本中的每个字符位置会首先匹配该正则表达式的所有可能排列,如果都不能匹配,然后才会到下一个字符位置进行匹配尝试.

例: 正则表达式: fat|cat|belly|your 目标文本:

The drgging belly in decates that your cat is too fat 如果global属性False,最后结果是:belly. 为True,结果是:belly cat your cat fat

从这个结果中看到了吗?匹配结果单词出现的顺序并不是正则表达式的分支排列顺序,而是目标文本中出现的顺序.

分析过程:在”The drgging “之前的每个位置上,匹配都是失败的.这时,引擎移动到下一位置,即目标文件”belly”的”b”前面,而该位置上<>不能匹配b,结果失败,这时用下一个子表式的开始字母<>与”b”匹配,还是失败;当用再下一子表达式(belly)的开始字母”b”尝试时,匹配成功,接着测试正则后面的字符,最终在该位置上得到成功匹配”belly”,引擎在该位置上停止,它不会在此位置再去尝试下一个子表式.所以,我们得到了第一个结果是”belly”.

例:正则表达式

jane|janet 或 janet|jane 分别匹配目标文本: janet and jane

朋友们可以自己测试两种匹配结果.并分析原因. 结论:

43

VBA平台的正则学习参考资料

1. 正则匹配总是优先选择目标文本中最左端的匹配结果.

2.如果在目标文本的同一位置存在两个选择分支都可匹配的时候,分支的排列顺序将影响匹配结果.你可增加单词边界或改变排列顺序来达成自己的目标.

3.将目标文本中最可能会被找到的字符放在前面,会在性能方面有较小的提高.

3. 匹配优先量词总是匹配尽可多的字符

匹配优先量词: ? * + {n} {n,m}

它们存在匹配上限和下限,总是匹配尽可多的字符,直到”匹配上限”或没有符合要求的字符为止. 例: 正则表达式: .? 目标文本: Abc

选项:global为false 匹配结果是: A

分析: ?表示匹配0个或1个除换行符的任意字符,由于它是匹配优先,所以,选择了匹配1个字符,而没有选择匹配0个.

例:正则表达式 \\d+ 目标文本: March 1998 匹配结果:1998

分析:在位置6上成功匹配字符”1”,而”+”表示可以无限地继续匹配下去,所以直到字符”8”后面位置上匹配失败. 但”+”的意思是只要能匹配上一个字符就算匹配成功,所以引擎最后报告匹配成功,得到上面的结果.

例:正则表达式 ^Subject:(.*) 目标文本: Subject:

匹配结果:Subject:

44

正则表达式入门与提高

$1变量值为空.

分析:目标文本的开始位置直到”:”号,引擎只进行简单的逐一字符比较,每个位置上都是匹配成功的. 但正则表达式还有”.*”部分(注:括号不会改变匹配过程),而目标文本已经没有字符可匹配了,但”*”表示即使匹配到0个字符(即没有得到匹配)也是成功的.所以,引擎报告匹配成功,得到如上的结果.(如果正则表达式中用?代替*,是一样的结果;但如果用”+”代替”*”,则整个匹配过程失败,无匹配结果,你能分析原因吗?)

再给一个例了: 正则表达式: ^.*(\\d+) 目标文本: Copyright 2014

你能推测最后括号捕获到的结果吗?也就是$1的值是什么呢?

它不是我们想像的”2014”,而是只得到一个字符”4”.为什么是这个结果呢?

分析: “^.*” 中”*”号的贪婪性(人性?)会将目标文本的所有字符匹配,直至结尾,引擎检查”\\d+”时,结果没有字符可匹配了.引擎这时是否就报告整个正则表达式匹配失败呢?记得上面讲到一个匹配总原则:”引擎总是穷尽所有途径,找到匹配”. 根据这个原则,正则中,每个部分的匹配只要不越过它们的”底线”,就尽可能让整个匹配成功.所以”.*”首先交还一个字符”4”,让”\\d+”匹配,由于”\\d+”的底线是匹配一个字符就可成功,而对”.*”来说少一个字符没有导致自己匹配失败.引擎这时发现每部分都匹配成功了,赶快报告匹配结果吧.于是就有了上面的结果.

如果这个例子中正则表达式是 ^.*(\\d{4}) 或 ^.*(.*)

情况结果又如何呢?

结论:最后的几个例子告诉我们:慎用表达式”.*”或”.+”,在编写正则表达式时,如果可能尽量用较具体的表达式代替\如:上例,假如我们希望的结果是提取\那么,可用\\D+代替\

4 . 忽略优先量词总是匹配尽可能少的字符

忽略优先量词: ?? *? +? {n}? {n,m}?

它们忠实贯彻”总设计师”的战略决策:”摸着石头过河!”.在匹配的过程中,它们总是自己先得到最少匹配,观察一下,正则表达式中,紧跟它后面的元素或子表达式,如果后面子表达式不需要(不能匹配),自己才

45

VBA平台的正则学习参考资料

继续匹配下一个字符.

例:正则表达式: <.*?> 目标文本: aaa 匹配结果:

分析: 在开始位置,”<”匹配成功,接下来”.*?”部分以最少匹配0个字符(即不匹配),宣布匹配成功;让它后面紧跟的”>”部分尝试匹配目标文本中的”e”,显然”>”匹配失败,于是,”.*?”选择匹配一个字符,从而成功匹配\引擎移到下一位置,先让”>”又尝试,还是失败,于是”.*?”再匹配”m”,匹配成功;移到下一位置,用”>”匹配目标文本在该位置上”>”,匹配成功.到此,正则表达式中的所有子表达式都得到了成功匹配,所以,引擎宣布整个匹配结束.结果.

当然,如果global属性设置为True,那么引擎将一如既往进行下去.从而,又得到一个结果 思考:用正则表达式 .*?

匹配文本:12d 匹配结果是什么? 如果用正则表达式 .*?d

匹配结果又是什么?

结论: 对忽略优先量词来说,只有因为它们匹配得太少,导致整个匹配失败的时候,它们才会匹配更多的字符.这个结论的意义是:在编制正则表达式时,注意避免各子表式都是可选项的情形.

四、穷尽所有可能途径找到匹配---回溯

实际上NFA能够找到所有可能的匹配,要归功于正则的一个最重要原理----回溯

如果正则表达式中,存在量词(无论匹配优先还是忽略优先)或多选结构,那么可能会产生”回溯”. 我们再重新审视引擎遭遇它们时的状况:

(1)多选结构的回溯

用cat|car匹配文本car and cat

根据前面知识分析,它将首先匹配到文本开始处的car, 前面说了原因:引擎会在每个位置上尝试正则表

46