本帖最后由 liu-aguang 于 2014-6-18 12:13 编辑
五、匹配一对特殊字符界定的之间的字符串,但其内部包含两端的界定字符 我们要匹配一对双引号包围的内容,但被包围的内容中含有双引号;匹配一对括号内的内容,但括号内又嵌套括号……看下面的例子:(提示,如果直接输入VBE代码中进行测试,注意在字符变量与正则表达式中双引号的转义) 例1 目标文本 ….Needs a “2 \” x3 \” likeness” of … 要求匹配结果: “2 \” x3 \” likeness” 正则表达式:
“”(\\.|[^\\””])+””
显然,不能用以前用过的办法:如””[^””]””或””.*?”” , 也不能用””.*”” ,因为文本的其它地方还可能出现双引号对. 编写正则表达式的重要技巧之一是: 集中关注在特定时刻真正容许匹配的字符.观察文本中匹配内容中的双引号特点:它始终与反斜线联结在一起.于是情况变得明朗起来,匹配内容实际上是由两部分组成的:一部分是 \” ,即反斜线与双引号的组合;一部分是非反斜线和非双引号的任意字符. 用正则来表达这个意思就是:\\.|[^\\""]我们用分组括号包围它们,然后用量词”+”作用(\\.|[^\\""])+,表示可以重复匹配无限次这样的字符[^\\””]或字符串\\. ,直到它遭遇失败. 现在我们来看它的匹配过程:当在….Need a 后的位置上时,它成功匹配双引号,接着尝试(\\.)部分与文本中”2”匹配失败,于是尝试[^\\””],匹配成功,重复分组,继续下一个位置......当引擎移动到likeness后的位置上时,此时文本的字符是一个双引号,而双引号既不能与(\\.)匹配也不能与[^\\””]匹配,所以此时正则括号部分匹配失败,回溯,而括号的作用量词是”+”,引擎报告整个量词作用的括号部分匹配成功,最后用双引号匹配文本中的双引号成功,完成整个匹配. 再给一个例子,请你自己尝试一下:匹配input.目标文本: …..<input name=dir value=”>”>….. 例2 目标文本 Val=foo(bar(this),3.7)+2*(that-1) 要求:提取文本中括号中的内容,即提取出(bar(this),3.7)和(that-1) 仿例1的思路,真正要匹配的是两部分:一是内嵌的(…)部分; 二是其它普通字符.正则表达式:
\(([^()]|\([^)]+\))*\)
匹配内嵌的(…)部分可用表达式:\([^)]+\);第二部分怎样表达呢?我们用了子表达式[^()]. 因为一当遇到内嵌”(“时,引擎将尝试第二个子表达式,即: \([^)]+\) ,该子表达式会一口气把内嵌括号匹配完.所以整个正则表达式能顺利完成任务. 我们能否用下列正则表达式呢? \(([^()]*|(\([^)]+\))*)*\) 这个表达式用量词*号作用[^()],可以一次把所有内嵌括号前或后的字符匹配完成,从而减少每次进入退出正则括号的处理次数;同样道理,用”*”作用\([^]]+\)部分,处理有多个内嵌括号的情况.这个主意看来不错.但这是绝对不行的,原理部分讨论过一个问题:灾难回溯.而这个表达式就符合灾难回溯的特征.量词被量词作用. 不过,下面这个表达式应该是一个效率更高的正则表达式: \([^()]*(\([^()]*\))*[^()]*\) 请自己解读吧.
例3 下面是一个由excel转换为csv文件格式的文本 Ten Thousand,1000,2710,,"10,000","It’s ""10Grand"",baby",10K 说明:excel转存为csv文本,如果原来内容中有双引号,它将自动转换为重复的双引号. 要求:提取由逗号分隔的各项数据 为了更清晰一点下面列出各项数据(共七项): The Thousand 1000 2710 空值 “10,000” “It’s “”10 Grand””,baby” 10k 正则表达式: (?:^|,)(?:""((?:[^""]|"""")*)""|[^"",]*) 除第一个数据项外,其所有数据之前都有逗号分隔,所以我们可以用”^”或”,”来分别锁定各数据,即用(?:^|,)(….)形式分别提取各数据项,最后的结果中可能含有”,”,容易用VBA代码处理. 第二个问题是如何表达各数据项,在技巧一中,已经知道如果匹配项呈现多种状态,可以用多选结构与合适的量词来处理.现在来看它们的各种形态:一是由双引号包围的内容形式; 二是空值; 三是除一二外的形式. 第一种形态可以用例1,例2的方法:关注双引号内的字符实际只有两种类型即连续的双引号,转义后要用””””表达,其次是非双引号的字符用[^””]表达. 于是第一种形态可以表达为 “”(([^””]|””””)*)””,在本例中它可以匹配”10,000” 和"It’s""10 Grand"",baby" 第三种形态实际就是非双引号[^””],而第二种形态空值,可与第三种形态合并即[^””]* ;量词”*”,可以表示不出现任何字符. 把它们整合起来就是: (^|,)(“”(([^””]|””””)*)””|[^””]*) 这个表达式还有一个问题,即^或,匹配成功后,一当遇到非双引号部分时即尝试[^””]*时,它会匹配后面所有的字符. 如在位置0处它会匹配整个双引号前的部分即: Ten Thousand,1000,2710,,.所以为了迫使它在遇到逗号时停止,我们可以在[^””]中增加一个逗号即[^””,]*.最后的结果是: (^|,)(“”(([^””]|””””)*)””|[^””,]*) 说明:你可以根据情况,将捕获性括号修改为非捕获括号. 讨论: 集中关注在特定时刻真正容许匹配的字符,经常会解决多数问题,在具体处理的时候又利用了前面讨论过的”拆分”思想.即把需要匹配的字符(串),拆分成互不包含的若干部分.最后用多选结构及量词组合起来. |