前言
密码复杂度校验是很常见的功能,一般用正则实现。
网上搜索到的内容,使用了正则的Lookaround
表达式,之前没有用过,这里记录一下
Lookaround
主要的内容来自这里,讲的很通俗易懂。这里做一下简单的翻译
Lookaround
包括Lookahead
和Lookbehind
两种匹配模式。
功能解释
有时候我们需要匹配一段文本,但是只有当这段文本有特定的前缀 或者后缀时才匹配到他
比如1 turkey costs 30€
,我们想获得这里的价格30
,这个数字的特点是,他有一个后缀€
这时候就可以用Lookahead
Lookahead
Lookahead
的语法是X(?=Y)
,它的意思是,查询X
,但是仅当X
的后面跟的是Y
时
当然,这里的X
和Y
都可以是正则表达式的匹配模式
以前面的需求为例,需要查询一个有€
后缀的数字,那么相应的正则表达式是d+(?=€)
。
let str = "1 turkey costs 30€";
alert( str.match(/d+(?=€)/) ); //30
需要注意的是Lookahead
语法匹配的后缀仅仅是检测使用,不会在返回结果(30)中
Lookahead
还可以支持更复杂的场景,比如X(?=Y)(?=Z)
,它的含义是:
- 找到
X
- 校验
Y
是否紧跟X
(否则丢弃) - 校验
Z
是否紧跟X
(否则丢弃) - 如果2和3都匹配,则
X
匹配,否则继续搜索
这种情况下,Y
和Z
不能是互斥的
举个例子,d+(?=s)(?=.*30)
,这里查找一个数字,这个数字后面紧跟一个空格,并且在这个数字后面的某个地方,有一个数字30``(?=.*30)
:
let str = "1 turkey costs 30€";
alert( str.match(/d+(?=s)(?=.*30)/) ); // 1
Negative Lookahead
如果我们想找一个数字,他后面不是€
符号呢?这时候可以用Negative Lookahead
模式:X(?!Y)
它的意思是,寻找X
,但是仅当X
的后面不是Y
的时候
let str = "2 turkeys cost 60€";
alert( str.match(/d+(?!€)/g) ); // 2
Lookbehind
Lookahead
检测的是后缀,而Lookbehind
检测的是前缀。
和Lookahead
类似,Lookbehind
也有Positive、Negative两种匹配方式:
- Positive lookbehind:
(?<=Y)X
, 仅当X
前面是Y
时匹配成功 - Negative lookbehind:
(?<!Y)X
, 仅当X
前面不是Y
时匹配成功
还是用之前的例子,把单位换成人民币¥
,这时候我们想要匹配¥30
,需要使用(?<=¥)d+
,匹配一个前缀是¥
的数字:
let str = "1 turkey costs ¥30";
alert( str.match(/(?<=¥)d+/) ); // 30
如果想要匹配数量,即没有前缀的数字,而非价格,那么可以使用(?<!$)d+
捕获组(Capturing Group)
之前提到一点,无论是Lookaround
还是Lookbehind
,匹配的内容都不包含前缀和后缀本身。
比如对于这个模式d+(?=€)
,€
就不在最后的结果当中。
如果想要在结果中添加€
,那么可以用括号把需要匹配的内容包括起来。
在下面这个例子里(€|kr)
就会和价格的数字一起返回:
let str = "1 turkey costs 30€";
let regexp = /d+(?=(€|kr))/; // €|kr周围加了括号
alert( str.match(regexp) ); // 30, €+
关于密码复杂度验证
回到最开始的目的,密码复杂度校验。要求是8~20位,包含字母、数字和特殊字符。
网上很容易搜到解决方案:^(?=.*[a-zA-Z])(?=.*[0-9])(?=.*[._~!@#$^&*])[A-Za-z0-9._~!@#$^&*]{8,20}$
整个表达式可以分为4部分
^(?=.*[a-zA-Z])
^(?=.*[0-9])
注意这里开头的^
就是前面提到的级联的用法,虽然^
不在这个表达式之前,但是匹配时也是这样检测的。^(?=.*[._~!@#$^&*])
[A-Za-z0-9._~!@#$^&*]{8,20}
其中1-3分别用来匹配字母、数字、特殊字符,这里用到的是Lookahead
后缀匹配,而匹配的内容是^
既字符串的开头。
所以1-3确保了不同类型字符串存在,4确保了字符串数量正确。