转:正则表达式的先行断言(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)没有问题,但(?<=abc)是不被支持的,特别是当表达式中含有|连接的分支时,各个分支的长度必须相同。之所以不支持变长表达式,是因为当引擎检查后行断言时,无法确定要回溯多少步。Java支持?、{m}、{n,m}等符号,但同样不支持、+字符。Javascript干脆不支持后行断言,不过一般来说,这不是太大的问题。
本文转自:
https://www.cnblogs.com/chip/p/4278135.html 。
转:正则表达式的先行断言(lookahead)和后行断言(lookbehind)的更多相关文章
- 正则表达式的先行断言(lookahead)和后行断言(lookbehind)
正则表达式的先行断言和后行断言一共有4种形式: (?=pattern) 零宽正向先行断言(zero-width positive lookahead assertion) (?!pattern) 零宽 ...
- [转]正则表达式的先行断言(lookahead)和后行断言(lookbehind)
正则表达式的先行断言和后行断言一共有4种形式: (?=pattern) 零宽正向先行断言(zero-width positive lookahead assertion) (?!pattern) 零宽 ...
- ErgExp-lookbehind assert(后行断言)
//先行断言:先遇到一个条件,判断后面的条件是否满足 let test = 'hello world' console.log(test.match(/hello(?=\sworld)/)) //后行 ...
- Regex: positive lookahead 先行断言____ 后行断言(lookbehind)
先行断言: /a(?=b)/ ,positive lookahead,a的后方必须是b才行 /a(?!b)/ ,negative lookahead,a的后方必须不是b才能匹配 如下图示: 来 ...
- 第11.20节 Python 中正则表达式的扩展功能:后视断言、后视取反
一. 引言 在<第11.19节 Python 中正则表达式的扩展功能:前视断言和前视取反>中老猿介绍了前视断言和前视取反,与二者对应的还有后视断言和后视取反. 二. (?<=-)后视 ...
- python正则表达式--分组、后向引用、前(后)向断言
无名.有名分组 (1)正则表达式—无名分组 从正则表 达式的左边开始看,看到的第一个左括号“(”表示表示第一个分组,第二个表示第二个分组, 依次类推. 需要注意的是,有一个隐含的全局分组(就是索引号为 ...
- python正则表达式(8)--分组、后向引用、前(后)向断言
无名.有名分组 (1)正则表达式—无名分组 从正则表 达式的左边开始看,看到的第一个左括号“(”表示表示第一个分组,第二个表示第二个分组, 依次类推. 需要注意的是,有一个隐含的全局分组(就是索引号为 ...
- 零宽断言 -- Lookahead/Lookahead Positive/Negative
http://www.vaikan.com/regular-expression-to-match-string-not-containing-a-word/ 经常我们会遇到想找出不包含某个字符串的文 ...
- 用fastreport在进行多列打印时,默认是先行后列,如何改成先列后行排记录?
例子程序中的6.fr3是在Page中设置 columns=2这样就是先行后列,7.fr3就是3列先列后行的例子 1 6 2 7 3 8 4 9 5 10 但如果 ...
随机推荐
- ashx将datatable返回json数据
1.直接使用JsonConvert.SerializeObject().将datatable放入 输出字符串 下面是测试:用webform+ashx作为接口. public class GetJso ...
- fork()系统调用后,对于open()前后父子进程的访问
一开始我也不是很懂,后来看了一篇别人的博客觉得写得特别好,现在拷贝下来分享一下. 如果换成write函数 如果换成write函数,先open()后fork(),那么父子进程共享文件描述符,即使在子进程 ...
- nagle 算法 tcp nodelay 以及 quick ack分析
后面详细分析 先上传 之前总结查看源码后的总结 Nagle算法的基本定义是任意时刻,最多只能有一个未被确认的小段.所谓"小段",指的是小于MSS尺寸的数据块,所谓"未被确 ...
- menuconfig
1. menuconfig 的存在意义 原由是 项目的 config 项太多了,需要一个人性化的方式设置. menuconfig 背后是一个应用程序,用户和该应用程序交互,完成 config 设置. ...
- python 《numpy》
import numpy as np 创建一个矩阵 array = np.array([[1, 2, 3], [3, 2, 1]]) print(array) # [[1 2 3] # [3 2 1] ...
- linux全局和个人配置文件说明
1.bash配置文件: 1).全局(bash的配置文件) 有 /etc/profile /etc/profile.d/* 与 /etc/bashrc 其实都是bash这个程序启动的时候会读取配置 ...
- Design Principle vs Design Pattern 设计原则 vs 设计模式
Design Principle vs Design Pattern设计原则 vs 设计模式 来源:https://www.tutorialsteacher.com/articles/differen ...
- 【Python】Json序列化和反序列化模块dumps、loads、dump、load函数介绍
1.json.dumps() json.dumps()用于将dict类型的数据转成str,因为如果直接将dict类型的数据写入json文件中会发生报错,因此在将数据写入时需要用到该函数. 转换案例: ...
- 协程实现爬虫的例子主要优势在于充分利用IO时间去请求其他的url
# 分别使用urlopen和requests两个模块进行演示 # import requests # 需要安装的 # from urllib.request import urlopen # # ur ...
- PHP一些不一样的思路
大多数来自p牛 SQL注入(left join) 源代码 <?php $link = mysqli_connect('localhost', 'root', 'root'); mysqli_se ...