正则中的问号

几乎每一个框架中都或多或少有正则表达式的出现,一个正则表达式,可以节省许多各种冗长的匹配代码。今天看到某一框架,突然里面的正则不是很复杂,于是想研究一下。发现看不懂!尤其是对里面的“?”。于是赶紧补充了一下知识,以此记之!

问号在正则中有许多用法,这里我就把我找到几个用法罗列一下,主要参考的:

“?”匹配前面的字符,0次或1次,相当于{0,1}

比如/e?le?/会匹配“angular”中的“l”,会匹配“angel”中的“el”,会匹配“angel”中的“el”。对字母“e”匹配0或1次。

非贪婪模式

至于什么是贪婪模式和非贪婪模式,先举个栗子,用栗子说明吧!
第一个例子:

1
2
3
var str = "abcabcabc";
var reg = /a.*c/;
reg.exec(str)[0];

以上会返回如下结果:

1
"abcabcabc"

第二个例子:

1
2
3
var str = "abcabcabc";
var reg = /a.*?/;
reg.exec(str)[0];

以上会返回如下结果:

1
"abc"

第一个例子是匹配“a“和”c“两个字母,以及两者之间的任意单个字符,它会从开始进行匹配,一直查找到字符串结束位置。而第二个例子跟第一个例子所要匹配的内容一样,但是它会在找到第一个相匹配的结果后就停止继续查找,所以结果只返回了第一个匹配的“abc”,而对于后续的字符,无论有多少匹配的,都不再进行查找。

通过两个形象的例子,我们可以知道,第一个例子为“贪婪模式”,第二个为“非贪婪模式”。尽管它们有多种叫法,但我们不必在乎它们的名字,只要知道它们是做什么的就可以了。

以上两个例子都有一个前提条件,就是“整个表达式匹配成功”。比如字符串“abcabcdabcde”;非贪婪模式下通过正则/a*?e/进行匹配,此时返回结果会是“abcabcabcd”。因为此时只有匹配到最后的“e”才算事成功匹配,所以在未匹配到“e”时,此正则还是会一直往下进行匹配。再看贪婪模式下,通过正则/a*?d/进行匹配,返回结果为“abcabcd”,此时尽管为贪婪模式,但当正则匹配到字母“d”之后,后面的字符串不再有字母“d”,所以此时只会返回包括“d”之前的字符。

“?”不仅可以用在“*“之后,还可以用在其它匹配优先量词之后,将其变成非贪婪模式。匹配优先量词,或者贪婪模式的量词,包括以下:

“{m,n}”、“{m,}”、“?”、“*”和“+”。

通过“?”转变之后:

“{m,n}?”、“{m,}?”、“??”、“*?”和“+?”。

综上可知,“?”可以用来进行非贪婪模式的匹配。贪婪模式是尽可能多的进行匹配,而非贪婪模式正好相反,是尽可能少的进行匹配。当然,这只是在应用层面上进行的理解,原理是两种模式是如何进行匹配的,请大家和我一起百度之,不,谷歌之!

(?:x)非捕获括号

是的,从名字看,这是讲的括号。并且,既然有非捕获括号,那就必然有捕获括号了。而捕获括号就是”(x)”,捕获并返回匹配“x”的项目,并且可以通过数组[1],….,[n],进行访问。

由此可见,此处的”?:”的作用,就是匹配“x”,但不返回匹配的结果,我们无法通过数组[1],….,[n],进行访问。

还是用例子说话吧。
如字符串:str = angularjs,首先用捕获括号reg = /(an).*js/,执行正则的reg.exec(str)方法,返回的结果为["angularjs","an"]。括号此处相当于子表达式,所以返回的不仅是整个正则表达式匹配的字符串,也返回了子表达式所匹配的字符串。此处我们可以使用数组索引[0]来获取其匹配的结果。而如果我们使用非捕获括号,则只会返回整个正则主表达式的匹配项,不会非捕获括号内的选项。例如同上面的例子,正则改为reg = /(?:an).*js,同样执行reg.exec(str),此时返回的结果则只有["angularjs"]。还有我发现,整个正则表达式只包含一个子表达式,没有其它匹配规则时,比如字符串“angularjs”,正则为“/(?:an)/”,此时即便为非捕获括号,但仍会返回匹配项“an”。也许是这种情况下将其看作主表达式看待了。

x(?=y)正向肯定查找

如果单看名字,永远也猜不出来是做什么的。它还有许多叫法,这里我是参考mozila开发文档里面的叫法。它的作用是查找“x”,但这个“x”的条件是,后面必须紧跟着“y”。来举一个例子吧!

1
2
3
4
5
var str1 = "world";
var str2 = "word";
var reg = /wor(?=d)/;
reg.test(str1); =>false
reg.test(str2) =>true

可以看出,匹配的“wor”,后面必须紧跟着字母“d”,所以str2是匹配成功的,str1是匹配失败的。

还可以这么做,把上面的正则修改一下,var reg = /wor(?=d|l)/,这样两个字符串就都匹配了。这条正则的意思是匹配“wor”,并且其后必须紧跟着字母“d”,或者字母“l”。我们也可以看出,“?=”后面只是起到修饰作用,并不会返回结果。

x(?!y)正向否定查找

通过上面的正向肯定查找,可以猜测出,有一个与其相对的否定查找。作用与上面的相反,是匹配“x”,当且仅当“x”后面不为“y”的时候。我们同样拿上面的例子,对正则表达式稍作修改:

1
2
3
4
5
var str1 = "world";
var str2 = "word";
var reg = /wor(?!d)/;
reg.test(str1); =>true
reg.test(str2) =>false

其结果正好相反了。通过上面的正向肯定查找,这里不难理解了。此处不再赘述。

以上就是我所查找到的有关“?”的几个主要的正则表达式。也许还有其他的吧。以上基于javascript语言进行的分析,其它语言中也许略有差异,我没有研究,有兴趣的可以查看一下。就到这里了,休息,休息一下!