匹配溢出问题在正则表达式当中算是比较常见的问题,它常常导致我们匹配结果莫名其妙的出错,本文专门为你讲解如何通过匹配不包含特定字符串的方法来解决这类问题。
那么,什么是匹配溢出呢?
下面我们来看个例子:
源文本:<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学习笔记《正则表达式匹配不包含特定字符串解决匹配溢出问题》