正则匹配原理之 逆序环视深入 .

说明:部分内容有待进一步研究和修正,因为最近工作太忙,暂时抽不出时间来,未研究过的可以跳过这一篇,想研究的不要被我的思路所左右了,有研究清楚的还请指正1 问题引出

前几天在CSDN论坛遇到这样一个问题:
var str="8912341253789";
需要将这个字符串中的重复的数字给去掉,也就是结果89123457。
首先需要说明的是,这种需求并不适合用正则来实现,至少,正则不是最好的实现方式。
这个问题本身不是本文讨论的重点,本文所要讨论的,主要是由这一问题的解决方案而引出的另一个正则匹配原理问题。
先看一下针对这一问题本身给出的解决方案。


代码如下:

string str = "8912341253789";
Regex reg = new Regex(@"((\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 = new string[] {"878", "9878" };
List<Regex> regs = new List<Regex>();
regs.Add(new Regex(@"(?<=(\d)\d*)\1"));
regs.Add(new Regex(@"(?<=(\d)\d*?)\1"));
regs.Add(new Regex(@"(?<=(\d))\d*\1"));
regs.Add(new Regex(@"(?<=(\d))\d*?\1"));
foreach (string s in source)
{
foreach (Regex r in regs)
{
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 匹配结果:
------------------------
源字符串: 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”。“\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”。“\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”。“\d”取得控制权后,向右尝试匹配,匹配“8”成功,将控制权交给“\d*?”,由于“\d*?”为非贪婪模式,会优先尝试忽略匹配,即不匹配任何内容,并记录回溯状态,此时“(\d)\d*?”匹配成功,那么“(?<=(\d)\d*?)”也就匹配成功,匹配结果为位置1,由于此处的子表达式“(\d)\d*?”为非贪婪模式,取得一个成功匹配项后,即交出控制权,同时丢弃所有回溯状态。由于前面捕获组1捕获到的内容是“8”,所以“\1”要匹配到“8”才能匹配成功,而此时位置1后面的字符是“7”,此时已无可供回溯的状态,整个表达式在位置1处匹配失败。
正则引擎传动装置向前传动,由位置2处尝试匹配,控制权交给“(?<=(\d)\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”。“\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”。“\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)”交出控制权,那么它所匹配的位置就已固定,“SubExp1”所匹配的内容也已固定,并且没有可供回溯的状态了。
3 逆序环视匹配原理总结
再来总结一下正则表达式“(?<=SubExp1)SubExp2”的匹配过程吧。逆序环视的匹配原理图如下图所示。

图3-1 逆序环视匹配原理图

正则表达式“(?<=SubExp1)SubExp2”的匹配过程,可分为主匹配流程和子匹配流程两个流程,主匹配流程如下图所示。

图3-2 主匹配流程图

主匹配流程:

1、 由位置0处向右尝试匹配,在找到满足“(?<=SubExp1)”最小长度要求的位置前,匹配一定是失败的,直到找到这样一个的位置x,x满足“(?<=SubExp1)”最小长度要求;
2、 从位置x处向左查找满足“SubExp1”最小长度要求的位置y;
3、 由“SubExp1”从位置y开始向右尝试匹配,此时进入一个独立的子匹配过程;
4、 如果“SubExp1”在位置y处子匹配还需要下一轮子匹配,则再向左查找一个y',也就是y-1重新进入独立的子匹配过程,如此循环,直到不再需要下一轮子匹配,子匹配成功则进入步骤5,最终匹配失败则报告整个表达式匹配失败;
5、 “(?<=SubExp1)”成功匹配后,控制权交给后面的子表达式“SubExp2”,继续尝试匹配,直到整个表达式匹配成功或失败,报告在位置x处整个表达式匹配成功或失败;
6、 如有必要,继续查找下一位置x',并开始新一轮尝试匹配。
子匹配流程如下图所示。

图3-3 子匹配流程图

子匹配过程:

1、 进入子匹配后,源字符串即已确定,也就是位置y和位置x之间的子字符串,而此时的正则表达式则变成了“^SubExp1$”,因为在这一轮子匹配当中,一旦匹配成功,则匹配开始位置一定是y,匹配结束位置一定是x;
2、 子表达式长度固定时,要么匹配成功,要么匹配失败,返回匹配结果,并且不需要下一轮子匹配;
3、 子表达式长度不固定时,区分是非贪婪模式还是贪婪模式;
4、 如果是非贪婪模式,匹配失败,报告失败,并且要求进行下一轮子匹配;匹配成功,丢弃所有回溯状态,报告成功,并且不再需要尝试下一轮子匹配;
5、 如果是贪婪模式,匹配失败,报告失败,并且要求进行下一轮子匹配;匹配成功,丢弃所有回溯状态,报告成功,记录本次匹配成功内容,并且要求尝试下一轮子匹配,直到取得最长匹配为止;
在特定的一轮匹配中,x的位置是固定的,而逆序环视中的子表达式“SubExp1”,在报告最终的匹配结果前,匹配开始的位置是不可预知的,需要经过一轮以上的子匹配才能确定,但匹配结束的位置一定是位置x。
当然,这只是针对特定的一轮匹配而言的,当这轮匹配失败,正则引擎传动装置会向前传动,使x=x+1,再进入下一轮匹配尝试,直到整个表达式报告匹配成功或失败为止。
至此逆序环视的匹配原理已基本上分析完了,当然,还有更复杂的,如“SubExp1”中既包含贪婪模式子表达式,又包含非贪婪模式子表达式,但无论怎样复杂,都是要遵循以上匹配原理的,所以只要理解了以上匹配原理,逆序环视也就没什么秘密可言了。

(0)

相关推荐

  • 正则表达式中环视的简单应用示例【基于java】

    本文实例讲述了正则表达式中环视的简单应用.分享给大家供大家参考,具体如下: 由于开发工作需要对文本中内容进行过滤,删除或替换掉一些无用的或不符合要求的信息.于是发现一个问题,某一类工程性文本中,用到很多英文写法相同.但含义不同的单位,需要将其分别转为真实含义对应的汉字.比如:"粘度为17s,移动距离为350厘米,要求混凝土必须内实外光.振捣时间为30s.",很明显第一个s是粘度的单位,第二s是时间单位,现在需要将文本中所有表示时间的s替换为"秒",在朋友指引下,发现

  • 正则表达式零宽断言详解

    正则表达式零宽断言: 零宽断言是正则表达式中的难点,所以本章节重点从匹配原理方面进行一下分析.零宽断言还有其他的名称,例如"环视"或者"预搜索"等等,不过这些都不是我们关注的重点. 一.基本概念: 零宽断言正如它的名字一样,是一种零宽度的匹配,它匹配到的内容不会保存到匹配结果中去,最终匹配结果只是一个位置而已. 作用是给指定位置添加一个限定条件,用来规定此位置之前或者之后的字符必须满足限定条件才能使正则中的字表达式匹配成功. 注意:这里所说的子表达式并非只有用小括号

  • 正则表达式断言、巡视(Assertions)、正向断言、反向断言介绍

    断言(Assertions)在正则表达式概念里面难理解,它通常指的是在目标字符串的当前匹配位置进行的一种测试但这种测试并不占用目标字符串,也即不会移动模式在目标字符串中的当前匹配位置.详细可以看看,正则表达式匹配解析过程探讨分析(正则表达式匹配原理),里面提到"零宽度"很多元字符,只是对特殊位置进行匹配,它们可以理解为断言. 断言元字符 常见断言元字符有: \b, \B, \A, \Z, \z, ^ ,$ 它们只是表示特殊位置,各自作用如有字符串AB,带位置表示为:0A1B2 元字符

  • javascript 正则表达式分组、断言详解

     javascript 正则表达式分组.断言详解 提示:阅读本文需要有一定的正则表达式基础. 正则表达式中的断言,作为高级应用出现,倒不是因为它有多难,而是概念比较抽象,不容易理解而已,今天就让小菜通俗的讲解一下. 如果不用断言,以往用过的那些表达式,仅仅能获取到有规律的字符串,而不能获取无规律的字符串. 举个例子,比如html源码中有<title>xxx</title>标签,用以前的知识,我们只能确定源码中的<title>和</title>是固定不变的.因

  • 正则应用之 逆序环视探索 .

    1 问题引出 前几天在CSDN论坛遇到这样一个问题. 我要通过正则分别取出下面 <font color="#008000"> 与 </font> 之间的字符串 1.在 <font color="#008000"> 与 </font> 之间的字符串是没法固定的,是随机自动生成的 2.其中 <font color="#008000"> 与 </font>的数量也是没法固定的,也是

  • 正则表达式之零宽断言实例详解【基于PHP】

    本文实例讲述了正则表达式之零宽断言.分享给大家供大家参考,具体如下: 前言 之前我曾写了一篇关于正则表达式的文章(http://www.jb51.net/article/111359.htm) 在该文章中详细介绍了正则,但是关于零宽断言介绍却是很少提及到.现在将该内容补充一下.在本文中,主要解决如下问题: ① 什么是零宽断言,为什么要使用零宽断言 ② 怎样使用零宽断言 概念 零宽断言,大多地方这样定义它,用于查找在某些内容(但并不包括这些内容)之前或之后的东西,也就是说它们像 \b ^ $ \<

  • 正则基础之 环视 Lookaround

    1       环视基础 环视只进行子表达式的匹配,不占有字符,匹配到的内容不保存到最终的匹配结果,是零宽度的.环视匹配的最终结果就是一个位置. 环视的作用相当于对所在位置加了一个附加条件,只有满足这个条件,环视子表达式才能匹配成功. 环视按照方向划分有顺序和逆序两种,按照是否匹配有肯定和否定两种,组合起来就有四种环视.顺序环视相当于在当前位置右侧附加一个条件,而逆序环视相当于在当前位置左侧附加一个条件. 表达式 说明 (?<=Expression) 逆序肯定环视,表示所在位置左侧能够匹配Exp

  • 正则表达式环视概念与用法分析

    本文实例讲述了正则表达式环视概念与用法.分享给大家供大家参考,具体如下: 1.环视又叫预搜索和零宽断言 2.环视又划分为 (?=exp)肯定顺序环视 (?<=exp)肯定逆序环视 (?!exp)否定顺序环视 (?<exp)否定逆序环视 3.环视只占用逻辑位置 不占用物理位置 如:匹配后缀名字为txt的文件 字符:file.txt.file2.exe 正则 \w(?=.exe) 匹配字符串file2 4.环视的用法 (?=exp)肯定顺序环视的2种用法 ① 查找电话号码是132开头的电话 字符:

  • 正则匹配原理之 逆序环视深入 .

    说明:部分内容有待进一步研究和修正,因为最近工作太忙,暂时抽不出时间来,未研究过的可以跳过这一篇,想研究的不要被我的思路所左右了,有研究清楚的还请指正1 问题引出 前几天在CSDN论坛遇到这样一个问题: var str="8912341253789"; 需要将这个字符串中的重复的数字给去掉,也就是结果89123457. 首先需要说明的是,这种需求并不适合用正则来实现,至少,正则不是最好的实现方式. 这个问题本身不是本文讨论的重点,本文所要讨论的,主要是由这一问题的解决方案而引出的另一个

  • 正则基础之 NFA引擎匹配原理

    1       为什么要了解引擎匹配原理 一个个音符杂乱无章的组合在一起,弹奏出的或许就是噪音,同样的音符经过作曲家的手,就可以谱出非常动听的乐曲,一个演奏者同样可以照着乐谱奏出动听的乐曲,但他/她或许不知道该如何去改变音符的组合,使得乐曲更动听. 作为正则的使用者也一样,不懂正则引擎原理的情况下,同样可以写出满足需求的正则,但是不知道原理,却很难写出高效且没有隐患的正则.所以对于经常使用正则,或是有兴趣深入学习正则的人,还是有必要了解一下正则引擎的匹配原理的. 2       正则表达式引擎

  • JS逆序遍历实现代码

    最常用的遍历方式为for语句(也有递归.while方式).当我们遍历一个数组的时候,我们一般会这么做: 复制代码 代码如下: var arr = [1,2,3,4,5,6,7,8,9,10]; for(var i=0,total=arr.length;i<total;i++){   console.log(i,arr[i]); } 这就是最常用的遍历方式:正序遍历.它从数组的第一项依次走到最后一项. 那为什么今天小剧还会提到逆序遍历呢? 这里不得不提下小剧写的组件里最常用的一个模块:events

  • C语言解字符串逆序和单向链表逆序问题的代码示例

    字符串逆序 上次面试碰到一个单向链表逆序的题目,幸好对字符串逆序比较熟悉,类比做出来了.字符串逆序比较简单,直接上代码: void stringReverse(char* p1,char* p2) { if(p1==p2)return; //swap the value of p1 ,p2 *p1=(*p1)+(*p2); *p2=(*p1)-(*p2); *p1=(*p1)-(*p2); if(p1==p2-1)return; else stringReverse(++p1,--p2); }

  • 面试题:用 Java 逆序打印链表

    昨天的 Java 实现单例模式 中,我们的双重检验锁机制因为指令重排序问题而引入了 volatile 关键字,不少朋友问我,到底为啥要加 volatile 这个关键字呀,而它,到底又有什么神奇的作用呢? 对 volatile 这个关键字,在昨天的讲解中我们简单说了一下:被 volatile 修饰的共享变量,都会具有下面两个属性: 保证不同线程对该变量操作的内存可见性. 禁止指令重排序. 共享变量:如果一个变量在多个线程的工作内存中都存在副本,那么这个变量就是这几个线程的共享变量. 可见性:一个线

  • MongoDB查询之高级操作详解(多条件查询、正则匹配查询等)

    MongoDB查询之高级操作 语法介绍 MongoDB查询文档使用find()方法,同时find()方法以非结构化的方式来显示所有查询到的文档. -- 1.基本语法 db.collection.find(query, projection) -- 返回所有符合查询条件的文档 db.collection.findOne(query, projection) -- 返回第一个符合查询条件的文档 -- query:可选,查询条件操作符,用于指定查询条件 -- projection:可选,投影操作符,用

  • 如何使用正则匹配最后一个字符串详解

    前几天遇到一个需求,输入的是 <user> <user> <name>a</name> </user> <user> <name>a</name> </user> </user> <password>123</password> 要求拿到 <user> <user> <name>a</name> </user&

  • Java实现单链表SingleLinkedList增删改查及反转 逆序等

    节点类 可以根据需要,对节点属性进行修改.注意重写toString()方法,以便后续的输出操作. //节点类 class Node { public int id; public String name; public Node next; public Node(int id, String name) { this.id = id; this.name = name; } @Override public String toString() { return "Node{" + &

  • URL @PathVariable 变量的匹配原理分析

    目录 URL @PathVariable 变量匹配原理 url 中带有变量的匹配原理 Demo 调试如下 总结 备注 @PathVariable @PathVariable 映射 URL 绑定的占位符 REST URL @PathVariable 变量匹配原理 url 中带有变量的匹配原理 在设置url的路径中我们可能使用变量来提高路径的灵活性,如 @RequestMapping(value="/{str}/qian",method=RequestMethod.GET) @Respon

随机推荐