正则表达式的先行断言(lookahead)和后行断言(lookbehind)

正则表达式的先行断言和后行断言一共有4种形式: 
(?=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是一个正则表达式。

如同^代表开头,$代表结尾,\b代表单词边界一样,先行断言和后行断言也有类似的作用,它们只匹配某些位置,在匹配过程中,不占用字符,所以被称为“零宽”。所谓位置,是指字符串中(每行)第一个字符的左边、最后一个字符的右边以及相邻字符的中间(假设文字方向是头左尾右)。 
下面分别举例来说明这4种断言的含义。

(?=pattern) 正向先行断言 
代表字符串中的一个位置,紧接该位置之后的字符序列能够匹配pattern。 
例如对”a regular expression”这个字符串,要想匹配regular中的re,但不能匹配expression中的re,可以用”re(?=gular)”,该表达式限定了re右边的位置,这个位置之后是gular,但并不消耗gular这些字符,将表达式改为”re(?=gular).”,将会匹配reg,元字符.匹配了g,括号这一砣匹配了e和g之间的位置。

(?!pattern) 负向先行断言 
代表字符串中的一个位置,紧接该位置之后的字符序列不能匹配pattern。 
例如对”regex represents regular expression”这个字符串,要想匹配除regex和regular之外的re,可以用”re(?!g)”,该表达式限定了re右边的位置,这个位置后面不是字符g。负向和正向的区别,就在于该位置之后的字符能否匹配括号中的表达式。

(?<=pattern) 正向后行断言 
代表字符串中的一个位置,紧接该位置之前的字符序列能够匹配pattern。 
例如对”regex represents regular expression”这个字符串,有4个单词,要想匹配单词内部的re,但不匹配单词开头的re,可以用”(?<=\w)re”,单词内部的re,在re前面应该是一个单词字符。之所以叫后行断言,是因为正则表达式引擎在匹配字符串和表达式时,是从前向后逐个扫描字符串中的字符,并判断是否与表达式符合,当在表达式中遇到该断言时,正则表达式引擎需要往字符串前端检测已扫描过的字符,相对于扫描方向是向后的。

(?<!pattern) 负向后行断言 
代表字符串中的一个位置,紧接该位置之前的字符序列不能匹配pattern。 
例如对”regex represents regular expression”这个字符串,要想匹配单词开头的re,可以用”(?<!\w)re”。单词开头的re,在本例中,也就是指不在单词内部的re,即re前面不是单词字符。当然也可以用”\bre”来匹配。

对于这4个断言的理解,可以从两个方面入手: 
1.关于先行(lookahead)和后行(lookbehind):正则表达式引擎在执行字符串和表达式匹配时,会从头到尾(从前到后)连续扫描字符串中的字符,设想有一个扫描指针指向字符边界处并随匹配过程移动。先行断言,是当扫描指针位于某处时,引擎会尝试匹配指针还未扫过的字符,先于指针到达该字符,故称为先行。后行断言,引擎会尝试匹配指针已扫过的字符,后于指针到达该字符,故称为后行。 
2.关于正向(positive)和负向(negative):正向就表示匹配括号中的表达式,负向表示不匹配。

对这4个断言形式的记忆: 
1.先行和后行:后行断言(?<=pattern)、(?<!pattern)中,有个小于号,同时也是箭头,对于自左至右的文本方向,这个箭头是指向后的,这也比较符合我们的习惯。把小于号去掉,就是先行断言。 
2.正向和负向:不等于(!=)、逻辑非(!)都是用!号来表示,所以有!号的形式表示不匹配、负向;将!号换成=号,就表示匹配、正向。

我们经常用正则表达式来检测一个字符串中包含某个子串,要表示一个字符串中不包含某个字符或某些字符也很容易,用[^...]形式就可以了。要表示一个字符串中不包含某个子串(由字符序列构成)呢? 
用[^...]这种形式就不行了,这时就要用到(负向)先行断言或后行断言、或同时使用。 
例如判断一句话中包含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”等。 
只要灵活运用这几个断言,就很容易解决: 
^(.(?<!that))*this(.(?<!that))*$ 
^(.(?<!that))*this((?!that).)*$ 
^((?!that).)*this(.(?<!that))*$ 
^((?!that).)*this((?!that).)*$ 
这4个正则表达式测试上述的几句话,结果都能够满足要求。

上述4种断言,括号里的pattern本身是一个正则表达式。但对2种后行断言有所限制,在Perl和Python中,这个表达式必须是定长(fixed length)的,即不能使用*、+、?等元字符,如(?<=abc)没有问题,但(?<=a*bc)是不被支持的,特别是当表达式中含有|连接的分支时,各个分支的长度必须相同。之所以不支持变长表达式,是因为当引擎检查后行断言时,无法确定要回溯多少步。Java支持?、{m}、{n,m}等符号,但同样不支持*、+字符。Javascript干脆不支持后行断言,不过一般来说,这不是太大的问题

时间: 2024-11-03 01:20:38

正则表达式的先行断言(lookahead)和后行断言(lookbehind)的相关文章

ErgExp-lookbehind assert(后行断言)

//先行断言:先遇到一个条件,判断后面的条件是否满足 let test = 'hello world' console.log(test.match(/hello(?=\sworld)/)) //后行断言 //判断world的前面是不是hello console.log(test.match(/(?<=hello\s)world/)) //匹配world前面不是hello的 console.log(test.match(/(?<!hells\s)world/)) 原文地址:https://ww

python正则表达式(8)--分组、后向引用、前(后)向断言

无名.有名分组 (1)正则表达式-无名分组 从正则表 达式的左边开始看,看到的第一个左括号"("表示表示第一个分组,第二个表示第二个分组, 依次类推. 需要注意的是,有一个隐含的全局分组(就是索引号为0的分组),就是整个正则 表达式匹配的结果 (2)正则表达式-有名分组 命名分组就是给具体有默认分组编号的组另外再起一个别名,方便以后的引用. 命令分组的语法格式如下: (?P<name>正则表达式) 语法格式中的字符P必须是大写的"P",name是一个合法

postgresql根据正则表达式切分,并获取切分后的某一个值

postgresql根据正则表达式切分,并获取切分后的某一个值 一.需求如下 出发地路线[白市驿收费站];通行路线[绕城高速—G65包茂高速—G65草坝场收费站—G65包茂高速—G42沪蓉高速];目的地路线[G42华蓥收费站]. 出发地路线[[白市驿收费站]];通行路线[[绕城高速—G65包茂高速—G65草坝场收费站—G65包茂高速—G42沪蓉高速]];目的地路线[[G42华蓥收费站]]. 目的:获取上述文字中的通行路线 二.postgresql切分函数简介 1.split_part 特点:可以

01 java断言assert初步使用:断言开启、断言使用

参考文件:http://blog.sina.com.cn/s/blog_59c9412d0100fd55.html 1 说明 java断言assert是jdk1.4引入的. jvm断言默认是关闭的. 断言可以局部开启的,如:父类禁止断言,而子类开启断言,所以一般说“断言不具有继承性”. 断言只适用复杂的调式过程. 断言一般用于程序执行结构的判断,千万不要让断言处理业务流程. 2 判断eclipse是否开启了断言 代码如下: public static void main(String args[

SQL 查询数据后行数据合并为列

SQL 将查询后得到的多行数据中的某一列转为一列,如原查找后的结果为: USER_NAME 张三 李四 王五 现要将其合并为: CUSTOMER 张三,李四,王五 SQL语句如下:select STUFF((select ','+USER_NAME from tbale where USER_NAME = '' for xml path('')), 1, 1, '') CUSTOMER 其中,stuff为sql封装好的一个方法函数,用于删除指定长度的字符并在指定的起始点插入另一组字符. 语法:S

正则表达式中圆括号的用法--也叫后向引用

所谓后向引用:一个正则表达式使用了圆括号()导致相关匹配将存储到一个临时缓冲区中,可以使用'\n'来访问其中的缓冲区,其中n为一个标识特定缓冲区的一位或两位十进制数,表达式中有过对()缓冲区将会有多个结果当然可以使用非捕获元字符 ?: , ?=  , ?! 来忽略对相关匹配的保存 问题: 如果不忽略这种缓冲区保存会有什么影响,什么时候要忽略保存? 答:正则表达式() 的用法,叫后向引用,也叫反向引用,就是将正则表达式的匹配结果临时存储起来,供以后调用  例1: var num = "1234 5

设计先行,编码在后

当晚,太太跟我交流要设计的工具很多东西没想清楚,没法写代码,方向感不强,于是我们花了些时间,把设计要点整理到纸上(请忽略这个广告纸) 因为我知道,今晚不搞清楚这个事情,想看个电影都难咯,^_^ 于是这篇文章,我花了些时间思考和写出来,几分钟可以读完. 设计先行 现实生活中房子,大多数开发商交楼的时候已经是带装修,业主索取设计图纸,才能得知水电布局,这个无可厚非:而对于整个小区,它是先呈现在设计规划图上,而不是建筑工人想到什么就建什么,必须按照设计图纸的要求来做,否则随意性带来的可能是灾害. 那么

正则表达式 零宽断言 负向零宽断言 平衡组/递归匹配

零宽断言 用于查找在某些内容(但并不包括这些内容)之前或之后的东西,像\b,^,$那样用于指定一个位置,这个位置应该满足一定的条件(即断言),因此它们也被称为零宽断言. (?<=exp)myexp(?=exp1) 负向零宽断言 与零宽断言类似 (?<!exp)myexp(?!exp1) 自己用上面的例子测试下 (?<=<(\w+)>).*(?=<\/\1>) 平衡组/递归匹配 这个看起来有点复杂,其实就是 push/pop. 这里很像编程语言,或者说像个语法分析器

log4j配置后行号乱码显示为?问号

debug="true"  classpathref="accrual.path" > 首发于 http://blog.xfuse.cn 参考文档 http://ant.apache.org/manual/CoreTasks/javac.html Log4j配置 log4j.appender.C1.layout.ConversionPattern=%F(%L)-- %-4r %-5p [%t] %37c %3x - %m%n 如果是用JAVAC编绎,那么直接加