锱铢必较:程序员生存指南——正则表达式中使用断言

想让一个名词听起来特别高大上,最简单的方法就是加很多修饰语。比如多源异构群智协同负反馈深度神经网络(当然了,这是我瞎编的)。在正则表达式中,有一种东西叫断言,它的修饰语也很多:

  1. 零宽正向先行断言
  2. 零宽负向先行断言
  3. 零宽正向后行断言
  4. 零宽负向后行断言

断言之所以叫“零宽”,是因为它们不会消费字符串,因为断言匹配的是位置。
断言之所以叫“断言”,是因为它们用来产生一个TrueFalse的判定结果。
正向和负向分别指的是“应该出现”和“不应该出现”。
先行和后行分别指的是“此位置之后”和“此位置之前”。

这些东西有哪些实际的用途呢?Talk is cheap,show you the code!注意:以下例子是用scala写的,这样就避免了java字符串中“”的转义。
锱铢必较:程序员生存指南——正则表达式中使用断言

负向断言例子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

常见的密码强度验证一般都要求:

  1. 8-12位
  2. 必须有大写字母
  3. 必须有小写字母
  4. 必须有数字
^(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9]).{8,12}

这个正则表达式还是挺有用的,说不定哪次面试就用上了呢!!!

相关推荐