# 位置匹配对应的练习题
# 验证密码问题
- 规则一:密码长度 6-12 位,由数字、小写字母和大写字母组成
- 规则二:至少包括 2 种字符。
# step 1
先写出满足规则一的正则表达式:
横向匹配:长度使用量词 {m,n} 即可; 纵向匹配:数字、小写字母、大写字母;
const regexp = /[0-9a-zA-Z]{6,12}/;
# step 2 判断包含某一种字符串?
假设要求必须包含数字,该怎么使用正则进行判断呢?
我首先想到的是量词 +, 即至少出现一次
const regexp = /[0-9]+/;
或者
const regexp = /[0-9]{1,12}/;
但是这种方式不能和 step 1 中的正则表达式合并起来,只能按照顺序逐个检测是否符合规则。
(这里不考虑最笨的暴力枚举)
作者的思路是通过位置来进行判断,即
var regex = /(?=.*[0-9])^[0-9A-Za-z]{6,12}$/;
为什么这个正则表达式可以呢?我们来分析下:
这里使用了正向先行断言,子模式是.*[0-9], 其中
.*表示任意字符出现任意次
和[0-9a-zA-Z]{0,11}的作用相同[0-9]表示数字
合并起来字面意思就是:寻找位于任意字符 + 数字前面的位置。
翻译成符合人类思维就是:step 1 中最后匹配的结果,需要存在符合子模式.*[0-9] 的字符串
存在符合子模式.*[0-9] 的字符串可以是下面的组合:
数字开头
1234ab🍬
var regex = /.*[0-9]/; var string = "1234ab"; console.log(string.match(regex)); // => ["1234"]非数字开头
ab1234🍬
var regex = /.*[0-9]/; var string = "ab1234"; console.log(string.match(regex)); // => ["ab1234"]数字结尾
1234ab12
ab12345🍬
var regex = /.*[0-9]/; var string = "1234ab12"; console.log(string.match(regex)); // => ["1234ab12"]非数字结尾
1234ab
ab1234ee🍬
var regex = /.*[0-9]/; var string = "ab1234ee"; console.log(string.match(regex)); // => ["ab1234"]从上面几个分类中可以看出,只要
step 1中最后匹配的结果中存在符合子模式.*[0-9]的字符串,那就一定有数字。# step 3 同时包含具体两种字符
step 2的巧妙在于,通过位置匹配来校验step 1的结果。同时包含小写字母和数字
var regex = /(?=.*[a-z])(?=.*[0-9])^[0-9A-Za-z]{6,12}$/;同时包含大写字母和数字
var regex = /(?=.*[A-Z])(?=.*[0-9])^[0-9A-Za-z]{6,12}$/;同时包含小写字母和大写字母
var regex = /(?=.*[a-z])(?=.*[A-Z])^[0-9A-Za-z]{6,12}$/;# 最终
🍬
var regex = /(((?=.*[a-z])(?=.*[0-9]))|((?=.*[A-Z])(?=.*[0-9]))|((?=.*[a-z])(?=.*[A-Z])))^[0-9A-Za-z]{6,12}$/; console.log( regex.test("1234567") ); // false 全是数字 console.log( regex.test("abcdef") ); // false 全是小写字母 console.log( regex.test("ABCDEFGH") ); // false 全是大写字母 console.log( regex.test("ab23C") ); // false 不足6位 console.log( regex.test("ABCDEF234") ); // true 大写字母和数字 console.log( regex.test("abcdEF234") ); // true 三者都有# 负向先行断言
至少包含两种字符,可以反向理解成:不能只有一种字符,就可以用到负向先行断言了。
- 不能全是数字
let regex = /(?!^[0-9]{6,12}$)^[0-9A-Za-z]{6,12}$/;- 不能全是小写字母
let regex = /(?!^[a-z]{6,12}$)^[0-9A-Za-z]{6,12}$/;- 不能全是大写字母
let regex = /(?!^[A-Z]{6,12}$)^[0-9A-Za-z]{6,12}$/;注意负向先行断言的子模式:
^[A-Z]{6,12}$,里面的^和$一定不能漏了因为期望
step 1的结果不符合子模式var regex = /(?!^[0-9]{6,12}$)(?!^[a-z]{6,12}$)(?!^[A-Z]{6,12}$)^[0-9A-Za-z]{6,12}$/; console.log( regex.test("ABCDEF234") ); // true 大写字母和数字如果没有
^和$,那么剩下的(?![A-Z]{6,12})实际上代表的是step 1的结果中不能含有符合子模式的字符串。var regex = /(?![0-9]{6,12})(?![a-z]{6,12})(?![A-Z]{6,12})^[0-9A-Za-z]{6,12}$/; console.log( regex.test("ABCDEF234") ); // false 大写字母和数字