正则表达式的先行断言和后行断言一共有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不支持后行断言。

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

  1. [转]正则表达式的先行断言(lookahead)和后行断言(lookbehind)

    正则表达式的先行断言和后行断言一共有4种形式: (?=pattern) 零宽正向先行断言(zero-width positive lookahead assertion) (?!pattern) 零宽 ...

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

    正则表达式的先行断言和后行断言一共有4种形式: (?=pattern) 零宽正向先行断言(zero-width positive lookahead assertion) (?!pattern) 零宽 ...

  3. ErgExp-lookbehind assert(后行断言)

    //先行断言:先遇到一个条件,判断后面的条件是否满足 let test = 'hello world' console.log(test.match(/hello(?=\sworld)/)) //后行 ...

  4. Regex: positive lookahead 先行断言____ 后行断言(lookbehind)

    先行断言: /a(?=b)/  ,positive lookahead,a的后方必须是b才行 /a(?!b)/   ,negative lookahead,a的后方必须不是b才能匹配 如下图示:  来 ...

  5. 第11.20节 Python 中正则表达式的扩展功能:后视断言、后视取反

    一. 引言 在<第11.19节 Python 中正则表达式的扩展功能:前视断言和前视取反>中老猿介绍了前视断言和前视取反,与二者对应的还有后视断言和后视取反. 二. (?<=-)后视 ...

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

    无名.有名分组 (1)正则表达式—无名分组 从正则表 达式的左边开始看,看到的第一个左括号“(”表示表示第一个分组,第二个表示第二个分组, 依次类推. 需要注意的是,有一个隐含的全局分组(就是索引号为 ...

  7. 用fastreport在进行多列打印时,默认是先行后列,如何改成先列后行排记录?

    例子程序中的6.fr3是在Page中设置 columns=2这样就是先行后列,7.fr3就是3列先列后行的例子 1     6 2     7 3     8 4     9 5     10 但如果 ...

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

    无名.有名分组 (1)正则表达式—无名分组 从正则表 达式的左边开始看,看到的第一个左括号“(”表示表示第一个分组,第二个表示第二个分组, 依次类推. 需要注意的是,有一个隐含的全局分组(就是索引号为 ...

  9. 零宽断言 -- Lookahead/Lookahead Positive/Negative

    http://www.vaikan.com/regular-expression-to-match-string-not-containing-a-word/ 经常我们会遇到想找出不包含某个字符串的文 ...

随机推荐

  1. ==和equals

  2. iOS开源项目集合一

    http://www.th7.cn/Program/IOS/201308/146283.shtml

  3. C++语言体系设计哲学的一些随想(未完待续)

    对于静态类型语言,其本质目标在于恰当地操作数据,得到期望的值.具体而言,需要: (1)定义数据类型 你定义的数据是什么,是整形还是浮点还是字符.该类型的数据可以包含的值的范围是什么. (2)定义操作的 ...

  4. Binary Tree Inorder Traversal 解题思路 ×

    问题: 非递归中序遍历二叉树 思路: 1.大循环,判断节点是否为空,栈是否为空 2.不为空:点进栈,向左走 3.为空:为空,出栈,读取值,向右走

  5. JavaScript各种遍历方式详解

    为了方便例子讲解,现有数组和json对象如下 var demoArr = ['Javascript', 'Gulp', 'CSS3', 'Grunt', 'jQuery', 'angular']; v ...

  6. 帝国cms7.0设置标题图片(缺失状态下)

    有时候因为我们没有设置标题图片,程序就会是使用自己的标题图片,这就是问题所在,现在有2个办法解决这个问题, [1]直接替换调程序的标签图片,但是这样的方法虽然简单,但是图片大小固定,要是每个模版的图片 ...

  7. 1.1机器学习基础-python深度机器学习

    参考彭亮老师的视频教程:转载请注明出处及彭亮老师原创 视频教程: http://pan.baidu.com/s/1kVNe5EJ 1. 课程介绍 2. 机器学习 (Machine Learning, ...

  8. WPF 核心体系结构

    WPF 体系结构 主题提供 Windows Presentation Foundation (WPF) 类层次结构,涵盖了 WPF 的大部分主要子系统,并描述它们是如何交互的. System.Obje ...

  9. 关于android app两次点击返回键退出的处理

    现在的android app在开发时,引入了两次点击返回键退出app的设计 为了避免用户误触,这个设计很人性化 中文网上社区有些同学贴了一些实现的例子,我觉得不是很好 代码如下 public bool ...

  10. C++中Reference与指针(Pointer)的使用对比

    整理日: 2015年3月18日 引用(reference)和指针(pointer)是学C++过程中最令人头疼的问题,常常不知道什么时候用哪个合适,又常常弄混.找到Dan Saks的这篇文章,讲的很清楚 ...