简述
前瞻和后顾是正则中比较难以理解的两个概念,也有人称作为正向零宽断言和负向零宽断言,还有叫做顺序环视和逆序环视。有很多种叫法,但是每一种叫法都让人难以理解。本文通过一张图示,带领大家彻底搞明白这个让人难以理解的概念。同时也会分享几个应用示例给大家,帮助大家以后写出更加简练的正则。
上面说的几种叫法,有前后、正反、顺逆几个关键字,这些表示的都是方向。本文最关键的地方就是要告诉大家这个方向到底是什么方向。相信大家看图也就知道了,这个方向代表的是正则沿着字符串匹配检测的方向。
从正在匹配检测的位置开始,其后方为已经被匹配检测的位置和字符,其前方为还未被匹配检测的位置和字符。正则匹配的方向是从左往右的,所以我个人觉得叫左视和右视会更合适一些,因为左右这样的描述是绝对的,而前后、正反、顺逆这样的描述都是相对的。如果把这个概念理解了,那么对前后、正反、顺逆的概念也就清晰了。
解读
有了方向的概念后,我们再来对名称来进行定义解读。
前瞻和后顾只会去匹配位置,不会匹配文本,不同的叫叫法实际都是表达的同一个一个意思,看大家怎么理解比较方便就怎么记名字。
与前瞻和后顾相似的还有负前瞻和负后顾,这里的负我们可以理解为非,就是不等于,只要把前瞻和后顾中的=
换成!
。
额外说明:后顾和负后顾在某些语言或者环境下不支持,使用时请谨慎验证支持情况。
示例
1、检测字符串是否包含abc
其实如果只是单纯的检测某个字符串是否包含abc
?用indexOf()
最好,不过这里还是分享一个正则校验的思路给大家。
如果只是写成/[abc]/
的话,这个集合内部是或
的关系。这只意味着字符串包含a
、b
、c
这三个中的任意一个,不能检测出字符串是否包含连续的abc
。
那我们要从另外一个角度去分析,找出字符串的任意一个位置右边是abc
即可(也可以用后顾找出一个位置左边是abc
,不过后顾支持率不好),我们可以利用前瞻的特性来实现这个正则/(?=abc)/
:
/(?=abc)/.test("cbacbac") //本字符串中不包含连续的abc,结果返回false
/(?=abc)/.test("cbacbabc") //本字符串中包含连续的abc,结果返回true
2、检测字符串是否包含连续两个相同的字符
good
、tomorrow
等有包含两个连续相同的字符,这样的校验用indexOf
是完成不了的,因为不能确定哪个字符连续出现了。这个例子是在第1个的基础上来的,因此我们直接修改第一个案例的表达式:
- 把
abc
换成一个任意字符:/(?=
.)/
- 把替换后的这个任意字符用分组捕获到:
/(?=(.)
\1)/
/(?=(\w)\1)/.test("good") //本字符串包含连续相同的字符oo,结果返回true
/(?=(\w)\1)/.test("cbacbabc") //本字符串中不包含连续的字符,结果返回false
3、检测字符串必须包含大写字符、小写字符和数字
做这个检测其实可以拆分为三个:包含大写字符、包含小写字符、包含数字,三个条件同时满足就通过。
//对字符串进行三个条件的校验,都满足时返回true
function test(str) {
return [/[A-Z]/,/[a-z]/,/\d/].every(reg => reg.test(str))
}
//或者对字符串进行三个条件的校验,只要一个不满足就返回false
function test(str) {
return ![/[A-Z]/,/[a-z]/,/\d/].some(reg => !reg.test(str))
}
test("aB123") //满足条件 结果返回true
test("ab123") //不满足条件 结果返回false
不过这样有个缺点,就是如果字符串中还包含其他的特殊字符也会校验通过。如果我们希望字符串只能由大写、小写以及数字组成,且每一种都要包含。应该怎么校验呢?
我们可以简单的分析一下隐藏的条件:
- 字符串包含大写、小写和数字,意味着从初始位置往后到结束都不能只由其中的一种或者两种来组成,即不能都是
[A-Za-z]
、[A-Z\d]
、[a-z\d]
,我们想到可以用负前瞻(匹配一个位置右边不能是啥) - 字符串只能由大写字符、小写字符和数字来组成
[A-Za-z\d]
- 大写、小写和数字每种都要包含,字符串长度最低是3
用正则去一步一步实现:
- 初始位置到结束都不能全是
[A-Za-z]
:/^(?![A-Za-z]+$)/
- 到结束也不能是
[A-Z\d]
,用或|
:/^(?!([A-Za-z]+
|[A-Z\d]+)$)/
- 到结束也不能是
[a-z\d]
,再用或|
:/^(?!([A-Za-z]+|[A-Z\d]+
|[a-z\d]+)$)/
- 只能由
[A-Za-z\d]
来组成:/^(?!([A-Za-z]+|[A-Z\d]+|[a-z\d]+)$)
[A-Za-z\d]/
- 字符串最低长度得是3:
/^(?!([A-Za-z]+|[A-Z\d]+|[a-z\d]+)$)[A-Za-z\d]
{3,}/
- 到结束都要满足条件:
/^(?!([A-Za-z]+|[A-Z\d]+|[a-z\d]+)$)[A-Za-z\d]{3,}
$/
var reg = /^(?!([A-Za-z]+|[A-Z\d]+|[a-z\d]+)$)[A-Za-z\d]{3,}$/
reg.test("ab123") //不满足条件,没有包含大写 结果返回false
reg.test("aB123") //满足条件 结果返回true
如果想对长度有要求可以再加上限制,比如要求长度在6-10位:
var reg = /^(?![A-Za-z]+$|[A-Z\d]+$|[a-z\d]+$)[A-Za-z\d]{6,10}$/
reg.test("aB123") //不满足条件,长度不够 结果返回false
reg.test("aBc123") //满足条件 结果返回true
reg.test("aBcD1234567") //不满足条件,长度超出 结果返回true
彩蛋
作为程序员,奔着能省一些代码就省一些代码的原则,[A-Za-z\d]
多少看起来有点冗余了,因为我们都知道\w
表示大写
、小写
、数字
和_
,\W
则表示取反。我们只是不需要\w
中的_
,因此我们可以利用负负得正的效果[^\W] === \w
,再把_
排除掉即可:
类似这样的操作还有,匹配一个大于0且小于32的正整数:
如果大家有正则表达式的需求,也可以在评论区留言,我会的尽量帮大家写出来。
常见问题FAQ
- 免费下载或者VIP会员专享资源能否直接商用?
- 本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
- 提示下载完但解压或打开不了?
- 找不到素材资源介绍文章里的示例图片?
- 模板不会安装或需要功能定制以及二次开发?
发表评论
还没有评论,快来抢沙发吧!