细说正则表达式之断言

最近遇到一个问题: 截取一篇文章中包含指定关键词的一句话(即前后可能有逗号或者句号分割)。方法有很多,单从实现的角度甚至可以用字符串的split方法先按照逗号和句号分割文章再遍历各个字符串检索关键词,但是实在是不太优雅^_^。当时脑中的第一反应就是正则表达式,那进入正题之前就先用这道小题热个身。
(注: 本文代码为C#代码)

假设要检索的关键词是keyword,则正则表达式为:
(^([^,\.])*?|(,|\.)[^,\.]*?)keyword.*?((,|\.)|$)
应该没什么问题吧。大概就是如果从头匹配,则keyword前需要匹配不含,.的任意字符;如果不是从头匹配的则keyword前必须有一个,或者.且keyword和该符号之间不能再包含,.。后半部分的意思是一样的就不多说了。
(注:\.为C#中匹配半角句号的写法;*?为非贪婪匹配)


进入正题

话说这道题貌似就到此为止了,那跟断言有什么关系呢?各位可以试一下,用这个方法匹配的结果前后基本都会带上逗号句号,也就是说虽然我们只要关键句,但是正则表达式把符号也匹配进来了。或许有人说替换一下就好啦,但是在这里我要强力推荐使用断言。理由有二,先说第一点: 断言更优雅简洁,逼格更高^_^

好啦,先让我们先来看看断言在这道题的写法吧。
(^[^,\.]*|(?<=,|\.)[^,\.]*?)keyword.*?(?=(,|\.)|$)

吼了,看看和上面的有什么不同吧。总共只有两部分:(?<=,|\.)以及(?=(,|\.),很像似不似。那为什么这两句话能达到我们想要的效果呢,先岔开话题说一下断言的分类:

(?=pattern)  零宽正向先行断言(zero-width positive lookahead assertion) 
(?!pattern)  零宽负向先行断言(zero-width negative lookahead assertion) 
(?<=pattern) 零宽正向后行断言(zero-width positive lookbehind assertion) 
(?<!pattern) 零宽负向后行断言(zero-width negative lookbehind assertion)

解释一下:

(?=pattern)  要求当前位置开始向后的字符串能匹配pattern。比如hello(?=world)匹配helloworld能够成功
(?!pattern)  要求当前位置开始向后的字符串不能匹配pattern。比如hello(?!world)匹配helloworld则失败
(?<=pattern) 要求当前位置开始向前的字符串能匹配pattern。比如(?<=hello)world匹配helloworld能够成功
(?<!pattern) 要求当前位置开始向前的字符串不能匹配pattern。比如(?<!hello)world匹配helloworld则失败

而除了对于前后字符串的限定之外,有没有注意到他们都是零宽的,换句话说断言只是条件,本身并不占位匹配。那么回头看看上面的小题。
我将前后部分的(,|\.)分别用(?<=,|\.)以及(?=(,|\.)进行替代。相当于把原本的匹配行为换成了条件判断行为: 前后包含逗号或句号则结果为true。这样一来,最终匹配结果就不会包含前后的符号了^O^ 。


那说到这里就引出了断言的第二点好处: 断言的条件判断可以允许我们匹配不包含指定关键词的句子↖(^ω^)↗
匹配包含指定关键词的正则表达式都见得多了,那不包含指定关键词呢。一般方法基本实现不了,或者比较麻烦,但是用断言的话就容易多了。借用网上的例子:

例如判断一句话中包含this,但不包含that。
包含this比较好办,一句话中不包含that,可以认为这句话中每个字符的前面都不是that或每个字符的后面都不是that。正则表达式如下:
^((?<!that).)*this((?<!that).)*$^(.(?!that))*this(.(?!that))*$
对于”this is the case”这句话,两个表达式都能够匹配成功,而”note that this is the case”都匹配失败。
在一般情况下,这两个表达式基本上都能够满足要求了。考虑极端情况,如一句话以that开头、以that结尾、that和this连在一起时,上述表达式就可能不胜任了。
如”note thatthis is the case”或者”this is the case, not that”或者”thatthis is the case”等。
只要灵活运用这几个断言,就很容易解决:
^(.(?<!that))*this(.(?<!that))*$
^(.(?<!that))*this((?!that).)*$
^((?!that).)*this(.(?<!that))*$
^((?!that).)*this((?!that).)*$
这4个正则表达式测试上述的几句话,结果都能够满足要求。

那么上面最后的几个正则表达式为什么能够成功呢,或者有人说这么复杂怎么记啊。其实很简单,上面四个表达式都是一个原理。先看看为什么^((?<!that).)*this((?<!that).)*$无法通过”note thatthis is the case”这句话的测试,因为^((?<!that).)*this只会判断每个字符的前面都不是that,但是由于判断时没有包括本字符,所以会出现判断thatthis这样的字符串时出错,倘若换成thatQthis就又没有问题了。包括^(.(?!that))*this(.(?!that))*$ 这个正则表达式也有类似的问题,就是以that开头和thisthat的模式就会出错。解决的思路就是把当前字符串也考虑在内,而这也是上面那四个正则表达式之所以没问题的原因啦


OK,断言的讲解到此为止~鞠躬~~

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
本课程是接口测试的基础+进阶+高阶的实战精品课程。 老师从多角度、多方位基于Demo进行讲解,力求做到认真细致讲好课程的每一要点。 全方位助力学员掌握接口测试的技术要领,快速跃升为接口测试方面的高手。 Demo具备很好的通用性及移植性,可以快速运用于实际接口测试中。 主讲老师为资深高级技术专家。毕业于电子科技大学。先后工作于多家知名外企,知名通信民企,知名IT公司。◇ 具备区块链领域高级测试解决方案设计、专家级测试开发、团队管理培训经验。◇ 具备专家级自动化测试解决方案、自动化测试平台框架设计开发、自动化测试团队管理培训经验。◇ 具备专家级的功能专项测试、性能专项测试经验及丰富的渗透测试经验。◇ 具备丰富的持续集成/持续交付(CI/CD)开发、实施及运维经验。◇ 具备丰富的C#/Java/Delphi/VB/C++/Python/Tcl/Groovy/Shell等开发及脚本开发经验◇ 具备20年的IT互联网工作经验。先后担任过项目经理,测试技术经理,测试总监以及专家级技术顾问等职务。独立开发完成有如下自动化测试框架及平台◇ GUI自动化测试框架(TSL)◇ ATF自动化测试框架(Tcl/Tk)◇ ATP自动化测试平台(C#/Java)◇ SoapUI接口自动化测试框架(Groovy/华为)◇ 持续集成自动化打包框架(Java/华为)◇ 区块链性能测试框架(Python)等。擅长框架/平台设计开发、团队管理、团队技能提升培训,技术瓶颈突破等。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值