MENU

正则表达式--(?= ?!及其系列)零宽断言 & 负向零宽断言

April 21, 2019 • 默认分类

请注意,本文最后修改于2019 年 11 月 11 日 16:37:34其中某些信息可能已经过时。

正向:

(?=exp) 零宽度正预测先行断言,它断言自身出现的位置的后面能匹配表达式。

(?<=exp) 零宽度正回顾后发断言,它断言自身出现的位置的前面能匹配表达式。

负向:

(?!exp) 零宽度负预测先行断言,它断言此位置的后面不能匹配表达式。

(?<!exp) 零宽度负回顾后发断言,它断言此位置的前面不能匹配表达式。

                (“exp”就是“表达式”的英文“expression”的缩写)

----------------------密封线内禁止答题----------------------

看到这些文绉绉的词汇,马上跪了,都是什么鬼意思。

首先,不管怎么说,该背的格式还是要背的。

其次,这些名词都是从英文硬拗过来的。先逐个拆解一下,希望对理解有帮助。没帮助的话,也没办法,死记硬背吧,多做练习吧,等到熟能生巧,这些名词都是浮云了。

----------------------密封线内禁止答题----------------------

零宽度
指的的是这些组合符号 仅仅匹配位置,而不匹配字符——换句话说,进行搜索和匹配的时候,他们不占用一个字符的位置,所以叫零宽度

正(向)& 负(向)要理解这个正负关系,最好先引入一个概念。我们把被开篇4个格式所作用的表达式称为断言式,比如“(?<=w+)”,这个“w+”就是断言式。所谓正,就是希望在被搜索的文本里有断言式匹配的部分;所谓负,就是希望在被搜索的文本里没有断言式匹配的部分。光这么说可能还不会很清楚,稍后会举例子说明,请结合例子来理解这段话。

预测先行 & 回顾后发 无力吐槽这鬼翻译,这差不多是我这辈子见过的最让人哭笑不得的翻译了。浓缩一下,大家只要关心“先”和“后”就行了。我们把一个表达式里不是断言式但与之位置上密切相关的部分称为匹配式(因为最终得到的匹配词,是由这部分来决定的,后面会举例说明),比如“b(?<=w+)d{4,6}b”这个表达式的匹配式就是“bd{4,6}b”。所谓先,就是在被搜索的文本里,匹配式(所匹配的内容)在断言式(所匹配的内容)前面;所谓后,就是在被搜索的文本里,匹配式(所匹配的内容)在断言式(所匹配的内容)后面;
对于先断言类型,匹配式要在断言式之前,位置密切相关;
对于后断言类型,匹配式要在断言式之后,位置密切相关。

浓缩成“先”和“后”之后这四个概念就变成:零宽正先断言,零宽正后断言;零宽负先断言,零宽负后断言——至少看起来没这么吓唬人了。

其实前面关于概念拆解的三条讲解,作为这个小节的总结更合适。现在来做例子,然后回过头再看看这三点,可能会更清楚些。

----------------------密封线内禁止答题----------------------

零宽正先断言(?=exp) 看这个表达式:bw+(?=ingsb) 用来匹配下面这句文本:

She keep drinking, screaming and crying all the night.

会得到什么样的匹配词?慢慢来分析。

表达式里,根据刚才所讲的知识,bw+ 是匹配式,它匹配一个以上的任意的数字或字母;ingsb 是断言式,它匹配字母ing加上一个空格;

零宽正先断言就是说,文本中有断言式的部分,且匹配式匹配的内容在断言式内容的前面——现在断言式的内容已经在文本中加黑标出(包括空格),单词 drinking 也有 ing ,但它后面跟的是逗号而不是空格,所以和断言式不匹配;那么显而易见,满足这两个条件的字符串是“scream”和“cry”,这就是最终得到的匹配词。

----------------------密封线内禁止答题----------------------

零宽负先断言(?!exp) 举例:表达式bwg(?!a)wb 匹配的内容,我们来分析一下。

显然“a”是断言式;对于先断言类型,匹配式要在断言式之前且位置密切相关,所以字符“g”是匹配式;前后两个“w*”都代表任意个字符串。

零宽负先断言就是说,文本中没有断言式(匹配字符“a”)的部分,即没有字符a;且匹配式(匹配字符“g”)在断言式前面。

这个怎么理解?匹配式在前,就是说断言式在后;但文本中又不能有字符“a”…… 一句话概括这两条信息就是:字符“g”后面不能跟着字符“a”。

所以这个表达式匹配的是字母 g 后面不跟着字母 a 的单词!比如句子:

The guy who is playing gameboy is a gay!

用这个表达式去匹配,将得两个结果:“guy”和“playing”。“gameboy”和“gay”因为 g 后面跟着 a,所以不匹配。

----------------------密封线内禁止答题----------------------

先消化一下,接着我们插播一点其他知识,邪恶地搞一下你的大脑:

之前我们学过关于反义的知识,用 ^exp符号表示反义,于是学得很扎实的同学会想,上面那个例子,写成 bwg1wb 不就行了,为什么要用什么零宽什么先后什么正负断言这些乱七八糟的东西呢?

想法是好的,可惜匹配的结果不是我们想要的。同样还是那句话,用反义匹配出来的结果是“guy”和“playing gameboy”…… ……Why ??

这是因为,1不是位置符,它可以匹配一个符号,标点也好,字符也好,空格也好,只要不是字符a,它就可以匹配。在“playing gameboy”里,playin 匹配了bw,紧跟着第一个g字符后面跟的是空格,不是a,所以是匹配1的;然后 gameboy 匹配了wb。这就是为什么“playing gameboy”会被匹配出来。

有人会问,gameboy里的 g 后面明明跟着 a 啊,怎么能出现在匹配词里?这就涉及到前面关于“贪婪匹配和懒惰匹配”里边“先开始的匹配拥有最高的优先权”的知识了。因为引擎从左到右扫描到“playing gameboy”时,发现这2个单词可以匹配表达式,就优先把它俩做为一个整体匹配了,“gameboy”虽然不符合我们预期,但被忽视掉了。

正因为零宽断言匹配的是位置,而不是字符,所以可以避免这些问题。现在是不是对“零宽”有了更深的体会?

详细解析了零宽正先断言和零宽负先断言,剩下的零宽正后和零宽负后就不费那么多笔墨了。这回只给例子,有兴趣的可以自己慢慢分析。

零宽正后断言(?<=exp)

举例:(?<=bun)w+b 匹配以un开头的单词的后半部分(除了 un 以外的部分),比如用来匹配 “You are so ungelivable” 时,它匹配“gelivable”。

零宽负后断言(?<!exp)

举例:(?<![a-z])d{7} 匹配前面不是英文字母的七位数字。

----------------------密封线内禁止答题----------------------

到此为止,正则表达式的初级教程就算讲完了。如果您还意犹未尽,那么可以继续读书学习。比如电子工业出版社出版的《精通正则表达式》,515页,满足您的各种需求;再比如……

说点正经的,下面2个URL,对于即时学习正则表达式,是有帮助的:

正则表达式在线测试:http://tool.chinaz.com/regex/

正则表达式在线语法:http://msdn.microsoft.com/zh-cn/library/ae5bf541(VS.80).aspx

本教程所有匹配示例均默认忽略英文大小写。

文章参考来源,部分本人修改:
http://blog.sina.com.cn/s/blog_12e4623a90101cqy0.html
https://blog.csdn.net/u011974797/article/details/71479456

----------------------密封线内禁止答题----------------------

要求:由数字和字母组成,并且要同时含有数字和字母,且长度要在8-16位之间。

^(?![0-9]+$)(?![a-zA-Z]+$)[0-9A-Za-z]{8,16}$

分开来注释一下:
^ 匹配一行的开头位置
(?![0-9]+$) 预测该位置后面不全是数字
(?![a-zA-Z]+$) 预测该位置后面不全是字母
[0-9A-Za-z] {8,16} 由8-16位数字或这字母组成
$ 匹配行结尾位置

注:(?!xxxx) 是正则表达式的负向零宽断言一种形式,标识预该位置后不是xxxx字符。

附:

  要求:可以包含数字、字母、下划线,并且要同时含有数字和字母,且长度要在8-16位之间。

  ^(?![0-9]+$)(?![a-zA-Z]+$)[0-9A-Za-z_]{8,16}$

  1. a
本文作者:Losir
本文链接:正则表达式--(?= ?!及其系列)零宽断言 & 负向零宽断言 - https://ionssource.cn/archives/18/
版权声明:如无特别声明,本文即为原创文章,仅代表个人观点,版权归 Losir 所有,未经允许不得转载!

Last Modified: November 11, 2019
开往-友链接力