想让一个名词听起来特别高大上,最简单的方法就是加很多修饰语。比如多源异构群智协同负反馈深度神经网络(当然了,这是我瞎编的)。在正则表达式中,有一种东西叫断言,它的修饰语也很多:
- 零宽正向先行断言
- 零宽负向先行断言
- 零宽正向后行断言
- 零宽负向后行断言
断言之所以叫“零宽”,是因为它们不会消费字符串,可以理解为断言匹配的是位置。
断言之所以叫“断言”,是因为它们用来产生一个True\False的判定结果。
正向和负向分别指的是“应该出现”和“不应该出现”。
先行和后行分别指的是“此位置之后”和“此位置之前”。
这些东西有哪些实际的用途呢?Talk is cheap,show you the code!注意:以下例子是用scala写的,这样就避免了java字符串中“\”的转义。
![6908832-3a261c976b6b5354.png](https://i-blog.csdnimg.cn/blog_migrate/4186f143726370b1452a099bfe42c694.png)
负向断言例子1
假设有几个文件全名:"file1.mp3","file2.bat","file3.txt",需要把英文句号之前的文件名提取出来。在这个过程中,需要忽略所有bat文件和mp3文件。
val pattern ="""(\w+)\.(?!bat|mp3)(\w+)""".r
val result = List("file1.mp3", "file2.bat", "file3.txt")
.flatMap(s => {
s match {
case pattern(name, _) => List(name)
case _ => Nil
}
})
这个负向先行断言意思是此位置(句号后面)后面的字符串不能匹配“bat|mp3”,也就排除了bat和mp3扩展名。
负向断言例子2
例如需要在标书中需要提取采购联系人的姓名。
采购人:大连理工大学
联系人:张三
这时采购联系人可以认为是张三
采购人:大连理工大学
代理机构:大连理工代理公司
联系人:李四
这时李四不能认为是采购联系人,他应该是代理机构联系人。(别问我为什么不用如日中天、如火如荼的自然语言处理,而非要用正则表达式作茧自缚)
这时的正则表达式为
(采购人)(?!.*代理机构).*?(联系人:)(?<name>\S+)
它要求“采购人”后面出现“联系人”,但是“采购人”后面不能有“代理机构”。
事实上,严格来说应该要求“采购人”和“联系人”之间不能有“代理机构”,anyway......who cares?
正向断言例子1
提取获取标书的开始时间。
需要购买标书的投标人,请于3月15日到26日登录某某网站
招标文件下载时间:北京时间3月15日至3月22日
这个正则表达式要求后面有日期,前面要出现“购买标书”、“招标文件下载”等关键词,这些关键词是特定名词和特定动词的组合。
(标书|招标文件|购买|下载).*(?<month>\d+)月(?<day>\d+)日
这个正则表达式问题在于名词和动词没有要求同时出现。
((标书|招标文件) .*(购买|下载).*)|((购买|下载).*(标书|招标文件) .*)(?<month>\d+)月(?<day>\d+)日
这个要求倒是严格了,但是也太长了吧,增加了名词和动词时修改也不方便啊,要改两个地方呢,容易出错。
(?<=(标书|招标文件).*)(?<=(购买|下载).*)(\d+)月(\d+)日
这个正向断言则解决了以上两个问题。
正向断言例子2
常见的密码强度验证一般都要求:
- 8-12位
- 必须有大写字母
- 必须有小写字母
- 必须有数字
^(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9]).{8,12}
这个正则表达式还是挺有用的,说不定哪次面试就用上了呢!!!