本帖最后由 liu-aguang 于 2014-6-15 21:28 编辑
(二)捕获性括号(…)与非捕获性括号(?:…).
1.捕获型括号(….)
前面已经多次遇见过捕获性括号”(…)”, 总结前面使用圆括号有二个理由: 1) 分组,标明量词的作用范围.或限制多选结构,标明选择符”|”的作用范围. 例:一文本中,有多行文本,提取行全部以”AA”开头的连续3行. 正则表达式: (^AA.*\n){3} ^AA 锁定行特征,即行开始处有字符AA; .* 匹配行中其它字符至换行符,英文句点”.”是不能匹配换行符的; \n 匹配换行符; ^AA.*\n 匹配一个完整行; (^AA.*\n){3} 用()分组标明后面量词{3}的作用范围,即行重复3次. 2) 捕获文本,将括号内容的内容保存在特殊变量中. 在VBA中,捕获性括号保存的内容有三种方式引用: (1) 在Replace方法中通过$1,$2…方式引用; (2) 在正则表达式中,通过\1,\2…方式引用. (3) 可以用匹配对象的Submatches集合索引号提取其内容. 3) 关于重复分组 如果在分组的结束之后放一个量词,那么整个分组就会被重复.如(abc){3}与abcabcabc是相同的. 那么最后捕获性括号捕获的内容是什么呢? 例:正则表达式 (\d\d){1,3} 目标文本:123456 最后匹配结果是123456.$1的值,即捕获性括号捕获到的内容是56.你可以编制代码测试,用submatches属性提取该值验证.得到这个结果的原因是: 分组的匹配在每次引擎退出该分组的时候被捕获,并会覆盖该分组在之前匹配的任何文本.(\d\d){1,3}会匹配一个包含2个,4个或6个数字的字符串.引擎会退出该分组3次.当这个正则表达式匹配到123456的时候,捕获分组中保存的是56,因为该分组的最后一次循环存储的是56.另外两次匹配12和34被覆盖了. 2.非捕获型括号(?:…) 捕获性括号保存内容时,是会付出处理成本的.所以,如果只需要让它起分组作用,而不必保存之中内容.正则表达式提供了一个非捕获性括号的字符序列:(?:….),在形式上比捕获性括号多一个”?:”,并紧跟在左括号之后,它们是一个整体. 例: (?:中国|China) (?:.*) 使用非捕获性括号可提高一定的匹配效率,特别是在使用循环的时候. 再次指出的是,虽然环视中也有圆括号,但在那里是非捕获性的.
(三)抑制量词的贪婪性: ?
前面已经知道,所有量词都是贪婪的,或称之为匹配优先的.他们总是匹配尽可能多的字符.下面例子可以了解什么是贪婪: 目标文本:“This is a <EM>first</EM> test” 要求:匹配提取标签<EM>或</EM> 正则表达式: <.*> 代码: Sub test() Dim mh, s$ s = "This is a<EM>first</EM> test" WithCreateObject("vbscript.regexp") .Pattern = "<.*>" .Global = True Set mh = .Execute(s) End With MsgBox mh(0) End Sub 讨论: 在正则表达式中,使用了量词"*".它表示可匹配0个或多个字符.执行该代码结果显示:<EM>first</EM>.为什么不是我们希望的<EM>或</EM >结果呢?下面分析它的匹配过程: 当用正则表达式"<.*>"作用于目标文本时,首先用"<"去目标文本中尝试,结果在"标签"后面找到了"<";紧接着开始在下一个位置尝试”.*”,"."可匹配任意非换行符,"*"可以表示可以连续0次或多次.由于量词"*"匹配是匹配优先的,当匹配模式”.*”时,它会匹配至行的末尾,直到遇到换行符为止.下来该尝试表达式中的">"了,结果余下的字符是换行符,不能匹配字符”>”,在此匹配失败; 在原理中我们会讲到,这时引擎会回退,直至回退到右边第一次出现”>”字符,匹配成功.最后报告成功匹配结果:<EM>first</EM> 为了解决这类问题,正则引入了一个元字符”?”,将它紧跟在量词之后,如”.*?”,则可抑制量词的贪婪性.让它变成忽略优先量词.忽略优先量词总是匹配尽可少的字符 同上例,正则表达式修改为: <.*?> 其它代码不变,最后结果显示:<EM>,得到了我们希望的结果.(提示:从效率上看,本例用正则表达式”<[^>]*>”效率更高.其原因会在”原理篇”中会谈到) 上面的例子告诉我们,在匹配优先的量词后,紧跟一个”?”,则变成了”忽略优先量词”.它们总是匹配尽可少的字符.再举一例,在字符串“oooo”中,“o+?”只匹配单个“o”,而“o+”匹配所有“o” |