细说正则表达式之断言
来源:互联网 发布:微信业务域名 编辑:程序博客网 时间:2024/06/06 01:36
最近遇到一个问题: 截取一篇文章中包含指定关键词的一句话(即前后可能有逗号或者句号分割)。方法有很多,单从实现的角度甚至可以用字符串的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,断言的讲解到此为止~鞠躬~~
- 细说正则表达式之断言
- 新手学js:正则表达式之断言
- 正则表达式之零宽度断言
- 正则表达式之断言的记忆
- 正则表达式之零宽断言详解
- 正则表达式之零宽断言
- 正则表达式 断言
- 正则表达式分组、断言
- 正则表达式讲解--断言
- 细说正则表达式
- 细说正则表达式上篇
- 细说正则表达式下篇
- 正则表达式之:零宽断言不『消费』
- 正则表达式--零宽断言
- 正则表达式--零宽断言
- Oracle不支持断言正则表达式?
- 正则表达式(四)------ 断言
- 正则表达式零宽断言
- 【厚积薄发】编程技术总结7—封装、继承、多态简介
- Java中的Lab和RGB的相互转换
- jdbc连接
- USEFUL ONE-LINE SCRIPTS FOR SED (Unix stream editor)
- Maven面试题
- 细说正则表达式之断言
- mysql查询按照指定字符串进行排序
- thinkphp验证码使用小案例
- Python定义类
- 500 G JAVA视频网盘分享
- PHP 使用 微信JSSDK 拍照选择图片接口 利用localid预览时 安卓手机正常显示 iOS手机不显示
- 一招解决WampServer服务器离线问题
- vue.js使用例子
- mysql的存储引擎与锁