02正则匹配原理之_第1页
02正则匹配原理之_第2页
02正则匹配原理之_第3页
02正则匹配原理之_第4页
02正则匹配原理之_第5页
已阅读5页,还剩6页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

说明:部分内容有待进一步研究和修正,因为最近工作太忙,暂时抽不出时间来,未研究过的可以跳过这一篇,想研究的不要被我的思路所左右了,有研究清楚的还请指正1问题引出前几天在CSDN论坛遇到这样一个问题:varstr="8912341253789";需要将这个字符串中的重复的数字给去掉,也就是结果89123457。首先需要说明的是,这种需求并不适合用正则来实现,至少,正则不是最好的实现方式。这个问题本身不是本文讨论的重点,本文所要讨论的,主要是由这一问题的解决方案而引出的另一个正则匹配原理问题。先看一下针对这一问题本身给出的解决方案。stringstr="8912341253789";Regexreg=newRegex(@"((\d)\d*?)\2");while(str!=(str=reg.Replace(str,"$1")))(}richTextBox2.Text=str;/*输出89123457*/基于此有朋友提出另一个疑问,为什么使用下面的正则没有效果"(?<=(?<value>\d).*?)\k<value>”由此也引出本文所要讨论的逆序环视更深入的一些细节,涉及到逆序环视的匹配原理和匹配过程。前面的两篇博客中虽然也有介绍,但还不够深入,参考正则基础之__环视和正则应用之一-序环视探索。本文将以逆序环视和反向引用结合这种复杂应用场景,对逆序环视进行深入探讨。先把问题简化和抽象一下,上面的正则中用到了命名捕获组和命名捕捉组的反向引用,这在一定程度上增加了问题的复杂度,写成普通捕获组,并且用“\d”代替范围过大的“・",如下(?<=(\d)\d*?)\1”需要匹配的字符串,抽象一下,取两种典型字符串如下。源字符串一:878源字符串二:9878与上面正则表达式类似,正则表达式相应的也有四种形式正则表达式一:(?<=(\d)\d*)\1正则表达式二:(?<=(\d)\d*?)\1正则表达式三:(?<=(\d))\d*\1正则表达式四:(?<=(\d))\d*?\1先看一下匹配结果:string[]source=newstring[]("878","9878"};List<Regex>regs=newList<Regex>();regs.Add(newRegex(矿(?<=(\d)\d*)\1"));regs.Add(newRegex(@"(?<=(\d)\d*?)\1"));regs.Add(newRegex(@"(?<=(\d))\d*\1"));regs.Add(newRegex(@"(?<=(\d))\d*?\1"));foreach(stringsinsource)(foreach(Regexrinregs)(richTextBox2.Text+="源字符串:"+s.PadRight(8,'');richTextBox2.Text+="正则表达式:"+r.ToString().PadRight(18,'');richTextBox2.Text+="匹配结果:"+r.Match(s).Value+"\n\n";}richTextBox2.Text+="\n";}/*输出源字符串:878正则表达式:(?<=(\d)\d*)\1匹配结果:8源字符串:878-正则表达式:(?<=(\d)\d*?)\1匹配结果:源字符串:878-正则表达式:(?<=(\d))\d*\1匹配结果:78源字符串:878-正则表达式:(?<=(\d))\d*?\1匹配结果:78源字符串:9878正则表达式:(?<=(\d)\d*?)\1匹配结果:源字符串:9878正则表达式:(?<=(\d))\d*\1匹配结果:78源字符串:9878正则表达式:(?<=(\d))\d*?\1匹配结果:78*/这个结果也许会出乎很多人的意料之外,刚开始接触这个问题时,我也一样感到迷惑,放了两天后,才灵机一触,想通了问题的关键所在,下面将展开讨论。在此之前,可能还需要做两点说明:1、下面讨论的话题已经与本文开始提到的问题没有多大关联了,最初的问题主要是为了引出本文的话题,问题本身不在讨论范围之内,而本文也主要是纯理论的探讨。2、本文适合有一定正则基础的读者。如果您对上面几个正则的匹配结果和匹配过程感到费解,没关系,下面就将为您解惑;但是如果您对上面几个正则中元字符和语法代表的意义都不清楚的话,还是先从基础看起吧。2逆序环视匹配原理深入正则表达式一:(?<=(\d)\d*)\1正则表达式二:(?<=(\d)\d*?)\1正则表达式三:(?<=(\d))\d*\1正则表达式四:(?<=(\d))\d*?\1上面的几个正则表达式,可以最终抽象为“(?<=SubExp1)SubExp2”这样的表达式,在做逆序环视原理分析时,根据“SubExp1”的特点,可以归纳为三类:1、逆序环视中的子表达式“SubExp1”长度固定,正则表达式三和四属于这一类,当然,这一类里是包括“?”这一量词的,但也仅限于这一个量词。2、逆序环视中的子表达式“SubExp1”长度不固定,其中包含忽略优先量词,如“*?”、"+?”、"{m,}?”等,也就是通常所说的非贪婪模式,正则表达式二属于这一类。3、逆序环视中的子表达式“SubExp1”长度不固定,其中包含匹配优先量词,“*”、"+”、"{m,}”等,也就是通常所说的贪婪模式,正则表达式一属于这一类。下面针对这三类正则表达式进行匹配过程的分析。2.1固定长度子表达式匹配过程分析2.1.1源字符串一+正则表达式三匹配过程源字符串一:878正则表达式三:(?<=(\d))\d*\1首先在位置0处开始尝试匹配,由"(?<=(\d))”取得控制权,长度固定,只有一位,由位置0处向左查找一位,失败,七?<=(\d))”匹配失败,导致第一轮匹配尝试失败。正则引擎传动装置向前传动,由位置1处尝试匹配,控制权交给“(?<=(\d))”,向左查找一位,接着将控制权交给“(\四”,更进一步的将控制权交给“\d”。“\d”取得控制权后,向右尝试匹配,匹配“8”成功,此时"(?<=(\d))”匹配成功,匹配结果为位置1,捕获组1匹配到的内容就是“8”,控制权交给“\d*”。由于“\d*”为贪婪模式,会优先尝试匹配位置1后面的“7”和“8”,匹配成功,记录回溯状态,控制权交给“\1”。由于前面捕获组1捕获到的内容是“8”,所以“\1”要匹配到“8”才能匹配成功,而此时已到达字符串结尾处,匹配失败,“\d*”回溯,让出最后的字符“8”,再将控制权交给“\1”,由“\1”匹配最后的“8”成功,此时整个表达式匹配成功。由于"(?<=(\d))”只匹配位置,不占有字符,所以整个表达式匹配到的结果为“78”,其中“\d*”匹配到的是“7”,“\1”匹配到的是“8”。2.1.2源字符串二+正则表达式三匹配过程源字符串二:9878正则表达式三:(?<=(\d))\d*\1这一组合的匹配过程,与2.1.1节的匹配过程基本类似,只不过多了一轮匹配尝试而已,这里不再赘述。2.1.3源字符串一+正则表达式四匹配过程源字符串一:878正则表达式四:(?<=(\d))\d*?\1首先在位置0处开始尝试匹配,由“(?<=(\d))”取得控制权,长度固定,只有一位,由位置0处向左查找一位,失败,“(?<=(\d))”匹配失败,导致第一轮匹配尝试失败。正则引擎传动装置向前传动,由位置1处尝试匹配,控制权交给“(?<=(\d))”,向左查找一位,接着将控制权交给“(\巧”,更进一步的将控制权交给“\d”。“\d”取得控制权后,向右尝试匹配,匹配“8”成功,此时"(?<=(\d))”匹配成功,匹配结是果为位置1,捕获组1匹配到的内容就是“8”,控制权交给“\d*?”。由于“\d*?”为非贪婪模式,会优先尝试忽略匹配,记录回溯状态,控制权交给“\1”。由于前面捕获组1捕获到的内容是“8”,所以“\1”要匹配到“8”才能匹配成功,而此时位置1后面的字符是“7”,匹配失败,“\d*?”回溯,尝试匹配位置1后面的字符“7”,再将控制权交给“\1”,由“\1”匹配最后的“8”成功,此时整个表达式匹配成功。由于"(?<=(\d))”只匹配位置,不占有字符,所以整个表达式匹配到的结果为“78”,其中“\d*?”匹配到的是“7”,“\1”匹配到的是最后的“8”。这与2.1.1节组合的匹配过程基本一致,只不过就是“\d*”和“\d*?”匹配与回溯过程有所区别而已。2.1.4源字符串二+正则表达式四匹配过程源字符串二:9878正则表达式四:(?<=(\d))\d*?\1这一组合的匹配过程,与2.1.3节的匹配过程基本类似,这里不再赘述。2.2非贪婪模式子表达式匹配过程分析2.2.1源字符串一+正则表达式二匹配过程源字符串一:878正则表达式二:(?<=(\d)\d*?)\1首先在位置0处开始尝试匹配,由"(?<=(\d)\d*?)”取得控制权,长度不固定,至少一位,由位置0处向左查找一位,失败,“(?<=(\d)\d*?)”匹配失败,导致第一轮匹配尝试失败。正则引擎传动装置向前传动,由位置1处尝试匹配,控制权交给“(?<=(\d)\d*?)”,向左查找一位,接着将控制权交给“(\四”,更进一步的将控制权交给“\d”。“\d”取得控制权后,向右尝试匹配,匹配“8”成功,将控制权交给“\d*?”,由于“\d*?”为非贪婪模式,会优先尝试忽略匹配,即不匹配任何内容,并记录回溯状态,此时“(\d)\d*?”匹配成功,那么“(?<=(\d)\d*?)”也就匹配成功,匹配结果为位置1,由于此处的子表达式“(\d)\d*?”为非贪婪模式,取得一个成功匹配项后,即交出控制权,同时丢弃所有回溯状态。由于前面捕获组1捕获到的内容是“8”,所以“\1”要匹配到“8”才能匹配成功,而此时位置1后面的字符是“7”,此时已无可供回溯的状态,整个表达式在位置1处匹配失败。正则引擎传动装置向前传动,由位置2处尝试匹配,控制权交给“(?<=(\d)\d*?)”,向左查找一位,接着将控制权交给“(\巧”,更进一步的将控制权交给“\d”。“\d”取得控制权后,向右尝试匹配,匹配“7”成功,将控制权交给“\d*?”,由于“\d*?”为非贪婪模式,会优先尝试忽略匹配,即不匹配任何内容,并记录回溯状态,此时"(\d)\d*?”匹配成功,那么"(?<=(\d)\d*?)”也就匹配成功,匹配结果为位置2,由于此处的子表达式“(\d)\d*?”为非贪婪模式,取得一个成功匹配项后,即交出控制权,同时丢弃所有回溯状态。由于前面捕获组1捕获到的内容是“7”,所以“\1”要匹配到“7”才能匹配成功,而此时位置2后面的字符是“7”,此时已无可供回溯的状态,整个表达式在位置2处匹配失败。位置3处的匹配过程也同样道理,最后“\1”因无字符可匹配,导致整个表达式匹配失败。此时已尝试了字符串所有位置,均匹配失败,所以整个表达式匹配失败,未取得任何有效匹配结果。2.2.2源字符串二+正则表达式二匹配过程源字符串一:9878正则表达式二:(?<=(\d)\d*?)\1这一组合的匹配过程,与2.2.1节的匹配过程基本类似,这里不再赘述。2.3贪婪模式子表达式匹配过程分析2.3.1源字符串一+正则表达式一匹配过程源字符串一:878正则表达式二:(?<=(\d)\d*)\1首先在位置0处开始尝试匹配,由"(?<=(\d)\d*)”取得控制权,长度不固定,至少一位,由位置0处向左查找一位,失败,“(?<=(\d)\d*)”匹配失败,导致第一轮匹配尝试失败。正则引擎传动装置向前传动,由位置1处尝试匹配,控制权交给“(?<=(\d)\d*)”,向左查找一位,接着将控制权交给“(\四”,更进一步的将控制权交给“\d”。“\d”取得控制权后,向右尝试匹配,匹配“8”成功,将控制权交给“\d*”,由于“\d*”为贪婪模式,会优先尝试匹配,并记录回溯状态,但此时已没有可用于匹配的字符,所以匹配失败,回溯,不匹配任何内容,丢弃回溯状态,此时"(\d)\d*”匹配成功,匹配内容为“8”,那么"(?<=(\d)\d*)”也就匹配成功,匹配结果是位置1,由于此处的子表达式为贪婪模式,“(\d)\d*”取得一个成功匹配项后,需要查找是否还有更长匹配,找到最长匹配后,才会交出控制权。再向左查找,已没有字符,“8”已是最长匹配,此时交出控制权,同时丢弃所有回溯状态。由于前面捕获组1捕获到的内容是“8”,所以“\1”要匹配到“8”才能匹配成功,而此时位置1后面的字符是“7”,此时已无可供回溯的状态,整个表达式在位置1处匹配失败。正则引擎传动装置向前传动,由位置2处尝试匹配,控制权交给“(?<=(\d)\d*)”,向左查找一位,接着将控制权交给“(\巧”,更进一步的将控制权交给“\d”。“\d”取得控制权后,向右尝试匹配,匹配“7”成功,将控制权交给“\d*”,由于“\d*”为贪婪模式,会优先尝试匹配,并记录回溯状态,但此时已没有可用于匹配的字符,所以匹配失败,回溯,不匹配任何内容,丢弃回溯状态,此时"(\d)\d*”匹配成功,匹配内容为“7”,那么"(?<=(\d)\d*)”也就匹配成功,匹配结果是位置2,由于此处的子表达式为贪婪模式,“(\d)\d*”取得一个成功匹配项后,需要查找是否还有更长匹配,找到最长匹配后,才会交出控制权。再向左查找,由位置0处向右尝试匹配,“\d”取得控制权后,匹配位置0处的“8”成功,将控制权交给“\d*”,由于“\d*”为贪婪模式,会优先尝试匹配,并记录回溯状态,匹配位置1处的“7”成功,此时“(\d)\d*”匹配成功,那么"(\d)\d*”又找到了一个成功匹配项,匹配内容为“87”,其中捕获组1匹配到的是“8”。再向左查找,已没有字符,“87”已是最长匹配,此时交出控制权,同时丢弃所有回溯状态。由于前面捕获组1捕获到的内容是“8”,所以“\1”匹配位置2处的“8”匹配成功,此时整个有达式匹配成功。演示例程中用的是Match,只取一次匹配项,事实上如果用的是Matches,正则表达式是需要尝试所有位置的,对于这一组合,同样道理,在位置3处,由于“\1”没有字符可供匹配,所以匹配一定是失败的。至此,这一组合的匹配完成,有一个成功匹配项,匹配结果为“8”,匹配开始位置为位置2,也就是匹配到的内容为第二个“8”。2.3.2源字符串二+正则表达式一匹配过程源字符串二:9878正则表达式二:(?<=(\d)\d*)\1首先在位置0处开始尝试匹配,由“(?<=(\d)\d*)”取得控制权,长度不固定,至少一位,由位置0处向左查找一位,失败,“(?<=(\d)\d*)”匹配失败,导致第一轮匹配尝试失败。正则引擎传动装置向前传动,由位置1处尝试匹配,这一轮的匹配过程与2.3.1节的组合在位置1处的匹配过程类似,只不过“(\d)\d*”匹配到的是“9”,捕获组1匹配到的也是“9”,因此“\1”匹配失败,导致整个表达式在位置1处匹配失败。正则引擎传动装置向前传动,由位置2处尝试匹配,这一轮的匹配过程与2.3.1节的组合在位置2处的匹配过程类似。首先“(\d)\d*”找到一个成功匹配项,匹配到的内容是“8”,捕捉组1匹配到的内容也是“8”,此时再向左尝试匹配,又找到一个成功匹配项,匹配到的内容是“98”,捕捉组1匹配到的内容也是“9”,再向左查找时,已无字符,所以“98”就是最长匹配项,"(?<=(\d)\d*)”匹配成功,匹配结果是位置2。由于此时捕获组1匹配的内容是“9”,所以“\1”在位置2处匹配失败,导致整个表达式在位置2处匹配失败。正则引擎传动装置向前传动,由位置3处尝试匹配,这一轮的匹配过程与上一轮在位置2处的匹配过程类似。首先“(\d)\d*”找到一个成功匹配项“7”,继续向左尝试,又找到一个成功匹配项“87”,再向左尝试,又找到一个成功匹配项“987”,此时已为最长匹配,交出控制权,并丢弃所有回溯状态。此时捕获组1匹配的内容是“9”所以“\1”在位置3处匹配失败,导致整个表达式在位置3处匹配失败。位置4处最终由于“\1”没有字符可供匹配,所以匹配一定是失败的。至此在源字符串所有位置的匹配尝试都已完成,整个表达式匹配失败,未找到成功匹配项。2.4小结以上匹配过程分析,看似繁复,其实把握以下几点就可以了。1、逆序环视中子表达式为固定长度时,要么匹配成功,要么匹配失败,没什么好说的。2、逆序环视中子表达式为非贪婪模式时,只要找到一个匹配成功项,即交出控制权,并丢弃所有可供回溯的状态。3、逆序环视中子表达式为贪婪模式时,只有找到最长匹配成功项时,才会即交出控制权,并丢弃所有可供回溯的状态。也就是说,对于正则表达式"(?<=SubExp1)SubExp2”,一旦"(?<=SubExp1)”交出控制权,那么它所匹配的位置就已固定,“SubExpl”所匹配的内容也已固定,并且没有可供回溯的状态了。3逆序环视匹配原理总结再来总结一下正则表达式“(?<=SubExp1)SubExp2”的匹配过程吧。逆序环视的匹配原理图如下图所示。

子匹配?<主匹配ThesourcetASubExpl$strin)SubExp子匹配?<主匹配ThesourcetASubExpl$strin)SubExp2]:'ThisisthesourceSubExpl图3-1逆序环视匹配原理图正则表达式“(?<=SubExp1)SubExp”的匹配过程,可分为主匹配流程和子匹配流程两个流程,主匹配流程如下图所示。了匹配岐卬F报告位TT*处壑个表认式虬鬼成功•山声位了版姓屈i个表让式匹配失收Kmr-K配曲加&哥.明\了匹配岐卬F报告位TT*处壑个表认式虬鬼成功•山声位了版姓屈i个表让式匹配失收Kmr-K配曲加&哥.明\轮子匹配■?控泪枚女船居维了・我站式T「始登试匹配向孔音检到Zf■谖入子匹白!山位W疯节佐查找到一个何骨F向右卉找.到个位置*主匹配流程:1、由位置0处向右尝试匹配,在找到满足“(?<=SubExp1)”最小长度要求的位置前,匹配一定是失败的,直到找到这样一个的位置x,x满足“(?<=SubExp1)”最小长度要求;2、从位置x处向左查找满足“SubExpl”最小长度要求的位置y;3、由“SubExpl”从位置y开始向右尝试匹配,此时进入一个独立的子匹配过程;4、如果“SubExpl”在位置y处子匹配还需要下一轮子匹配,则

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论