正则表达式中的子组模式

Posted PHP在线

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了正则表达式中的子组模式相关的知识,希望对你有一定的参考价值。


注意,这里的lt必须放在lte的后面,否则的话正则表达式解析器读到lt时分支就已经匹配成功了,那么lte就永远不会被匹配到。

三、非捕获子组

有些时候子组只是用来描述“分支”的匹配的,我们并不想让最后的$matches里面出现括号里的内容,此时可以用非捕获子组(?:)告诉正则表达式解析器,它不需要被捕获:

'#(?:https?|ftp)://([A-Za-z\.]+)#'


这样,URL里面主机名部分就会被存放至$matches数组下标为1的域内。而前面的https?|ftp虽然也被打了圆括号,但是由于圆括号中有?:,所以并不会被保存到$matches中。

不过这里仅仅是举例子,在实际应用中,可以调用parse_url函数来更好地完成获取主机名的任务。

四、前向探测(Lookahead)

前向探测的目的是,在当前的点,向后读入内容(对于读取匹配内容的程序来说,它即将读入的内容被称为“前”;但是对于阅读者来说,即将读入的内容被
称为“后”),判断其是否与子组中的正则表达式相匹配。如果匹配,则继续匹配后面的内容,否则匹配失败。虽然前向探测会向后读入内容,但是被读入的内容并
不会被“消耗”掉,也不算做正则表达式匹配的一部分,也就是说,后面的正则表达式依然可以匹配到向后读入的内容。

如果这样说不太明白,可以看看下面的例子。利用(?=)就可以构造一个前向探测:

'#\d*(?= mm)#'

这个正则表达式会匹配如'100 mm'这样的字符串。由于前向探测的正则表达式mm并不属于正则表达式的一部分,所以最后整个表达式(注意,不是$matches下标为1的域,而是整个表达式,也就是下标0)匹配出来的结果是'100'。

更好的例子是检查密码是否符合规范:

'#^(?=\w{8,20}$)(?=[^A-Z]*[A-Z])(?=[^a-z]*[a-z])(?=\D*\d)(?=[^_]*_).*$#'

这个正则表达式在最开头的地方依次使用了5个前向探测子组,分别检查密码长度在8至20之间、含有大写字母、含有小写字母、含有数字以及含有下划线。只有当这五个条件都满足,正则表达式才会继续向下匹配。由于这些子组都不会消耗读入的内容,所以最后我们简单地使用一个.*就可以获取整个密码字符串。

五、前向逆探测(Negative Lookahead)

与前向探测类似,只不过子组中的表达式必须不满足才行。它的构造方法为(?!):

'#\d*(?!\d| mm)#'


这个表达式除了类似于'100 mm'以外其余的类似于'100 cm'这样的字符串都可以被匹配。注意子组正则表达式里面加了一个\d,因为不加它,当读入'100 mm'的时候,表达式还是会匹配到'10',这是因为'0 mm'不匹配' mm'。

六、后向探测(Lookbehind)

与前向探测类似,后向探测只不过是以当前点为准,向前读入内容。后向探测的构造方法为(?<=):

'#(?<=EUR ).*#'

这个正则表达式会匹配'EUR 100'这样的字符串。匹配结果为'100'而不是'EUR 100',这是因为后向探测是以当前点为准,向前读入内容,这也就意味着,当开始进行最后.*的匹配时,'EUR '早已被读过了。

不过这并不意味着后向探测会消耗内容,只是因为我们并没有在正则表达式中匹配'EUR '而已。如果你有兴趣,可以尝试下面的表达式:

'#EUR (?<=EUR)\d*#'

这样,匹配出来的结果就是'EUR 100'了。

七、后向逆探测(Negative Lookbehind)

与后向探测类似,只不过子组内的表达式必须不匹配。这里就不再举例了。

八、命名子组

我们可以利用下面的语法命名一个子组:

'#(?P<prefix>A+)C#'


它会匹配类似于'AAAAC'的字符串,子组匹配的内容'AAAA'不仅会以数字下标保存(这个例子中为1),亦会以字符串下标('prefix')保存在$matches里面。

九、子组的重复利用

利用下面的方式我们可以重复利用已经在正则表达式中出现的子组:

'#(\w+) (?1)#'

这个正则表达式会匹配'foo bar'。不过需要注意的是,重用的子组并不会被捕获。如果想要捕获重用的子组,则应该在子组外面再加上一个括号:

'#(\w+) ((?1))#'

我们甚至可以通过子组名称来重复利用它:

'#(?<pattern>\w+) (?&pattern)#'

甚至还可以递归地调用子组:

'#(\w+, (?1)?)(\w+)#'

上面的表达式会匹配'foo, bar, baz, qux'。

十、重置分支

这一点在php官方文档中已经提到了:

'#(?:(Sat)ur|(Sun))day#'

当匹配'Sunday'的时候,我们会发现在$matches里面下标为1的域是空的,这是因为它尝试过匹配(Sat),由于没有匹配到内容,所以它在$matches里面加入了一个空的匹配项。如果要去掉这个恼人的匹配项,我们需要在匹配不成功的时候重置分支:

'#(?|(Sat)ur|(Sun))day#'

将原来的冒号改为竖线之后,我们就会发现,原来空的匹配不见了。

十一、总结

上面的文章中介绍了PCRE中子组的使用方法,并且简单地介绍了九种子组的特殊功能。如果能够灵活地、适当地运用在我们的程序中,它就可以帮助我们省掉许多字符串处理的步骤。



参考资料:http://www.rexegg.com/regex-disambiguation.html

 

以上是关于正则表达式中的子组模式的主要内容,如果未能解决你的问题,请参考以下文章

Python正则表达式

正则表达式小结

正则表达式匹配

c# 正则表达式捕获

正则表达式

JS和PHP的正则表达式的区别