那么,什么是匹配溢出呢?
下面我们来看个例子:
源文本:<div>ABC</div><div>123</div>
目标匹配数据:<div>123</div>
正则:<div.*?>\d+</div>
实际匹配:<div>ABC</div><div>123</div>
这个例子,我们匹配的数据偏移了目标匹配数据,但却包含目标匹配数据,我们就可以认为,前面部分的正则,因为通配符.*?
的限定范围比较大,对很多字符做了非预期匹配,也就是匹配溢出了。
对于这个问题,我们可以通过降低通配符的限定范围,从而使得它对于前面部分的匹配因为无法满足正则而退出,最终匹配到我们的目标数据。
因此,上面正则表达式可以修改为:
<div[^>]*?>\d+</div>
这样修改之后,通配部分最多只能匹配div标签里的到达>
前的字符,就不会怕它因为通配匹配掉下一个<div>
标签了。
因为上面式子中是对>
做了排除匹配,它无论如何也不会超出>
字符的范围,因此,上面正则从性能角度可以修改为:
<div[^>]*>\d+</div>
因为,假如<div>
标签当中还有其他的噪点数据如<div class="xxx">
,我们使用非贪婪模式,就会形成多次的回溯,降低性能。此时适当使用贪婪模式,可以让它里面匹配到<div
之后,>
之前的所有噪点数据,无需回溯,性能会有所提升。
上面例子是针对单字符,使用排除特定字符方法,限定通配符的匹配范围。而我们应用过程中,还经常会遇见多字符的排除匹配情况,下面我们来看看。
源文本:
http://www.zjmainstay.cn
http://www.baidu.com
http://www.qq.com
目标数据:每行一个域名,取出不是百度的域名
正则:/^http:\/\/((?!baidu).)+$/m
匹配结果:
http://www.zjmainstay.cn
http://www.qq.com
说明:正则里/m的m表示多行模式
可能很多人看到((?!baidu).)+
就立马懵逼了。其实,这个表达式也没那么难,大家可以这么理解:
假设是排除一个单字符>
,至少有一个字符,你是不是会写:[^>]+
没错,那我要求你一定要用否定正向环视去做呢?你会写成:(?!>).
限制当前位置后面的字符不能是>
,完全没有问题,上面两个是等价的。
那么,假设我限定当前位置后面不是baidu
呢?这时候,很明显对于单字符排除就不太好写了,因为我们知道正则是单字符匹配的,用单字符匹配就是排除各种b、a、i、d、u
的组合,想想都不可完成。但我们可以用否定正向环视轻松解决:(?!baidu).
那么问题来了,我要匹配这一串字符中每个位置都不能是baidu
,也就是:
(?!baidu).(?!baidu).(?!baidu).(?!baidu).(?!baidu).(?!baidu).(?!baidu).(?!baidu).(?!baidu).
谁知道有几个位置呢?(上面的(?!baidu).
个数仅做示例)
因此,根据题目,至少会有一个(?!baidu).
吧,因此,正则修改为:
((?!baidu).)+
也就得到了我们上面的式子了。此时的(?!baidu).
其实可以看做一次匹配,就是匹配.
,(?!baidu)
限定它与其后其他字符不能构成baidu
。
而((?!baidu).)
的括号只是把这个整体框起来,用于次数限定而已。
更多关于正则表达式入门的内容,请参考本站博客《我眼里的正则表达式入门教程》
更多关于正则表达式高级的内容,请参考本站博客《深入讲解正则表达式高级教程》
Windows正则表达式测试工具请从《正则表达式测试工具RegexBuddy-4.1.0》下载
Mac正则表达式测试工具请从《Mac正则表达式测试工具》下载
未经同意禁止转载!
转载请附带本文原文地址:正则表达式匹配不包含特定字符串解决匹配溢出问题,首发自 Zjmainstay学习笔记