正则表达式
正则表达式是匹配模式, 要么匹配字符,要么匹配位置
正则表达式位置匹配攻略
在使用环视的时候,
位置确定了可以通过位置来匹配位置后面(一般es5就是后面)的字符
字符确定了,就可以通过字符来匹配字符前面的位置
主要内容
- 什么是位置?
- 如何匹配位置
- 位置的特性
- 实例分析
1. 什么是位置?
位置是相邻两个字符之间的位置(当然包含两个边界)。
2. 如何匹配位置
在 ES5 中,共有 6 个锚字符(表示位置的字符):
^ , $ , \b , \B , (?=p) , (?!p)
2.1 ^ 和 $
^ (脱字符) 匹配开头,在多行匹配中匹配行开头
$ (美元符号) 匹配结尾,在多行匹配中匹配行结尾
比如我们把字符串的开头结尾用 “#” 替换 (位置是可以替换成字符的!)
var result = 'hello'.replace(/^|$/g , '#');
console.log(result);
// #hello#
多行匹配的时候,二者是行的概念,需要我们注意:
var result = 'i\nlove\njavascript'.replace(/^|$/g,'#');
// #i
// love
// javascript#
var result = 'i\nlove\njavascript'.replace(/^|$/gm,'#');
console.log(result);
// #i#
// #love#
// #javascript#
2.2 \b 和 \B
\b 是单词边界, 具体就是 \w 和 \W 之间的位置,也包括: \w 和 ^ 之间的位置,也包括 $ 和 \w 之间的位置。
比如: 文件名为 [JS] Lesson_01.mp4
中的 \b
var result = '[JS] Lesson_01.mp4'.replace(/\b/g,'#');
console.log(result);
// [#JS#] #Lesson_01#.#mp4#
\B 就是 \b 的反面, 非单词边界。例如在字符串中所有位置中,扣掉\b 剩下的就是 \B 位置
var result = '[JS] Lesson_01.mp4'.replace(/\B/g,'#');
console.log(result);
// #[J#S]# L#e#s#s#o#n#_#0#1.m#p#4
2.3 (?=p) 和 (?!p)
(?=p),其中 p 是一个子模式,即 p 前面的位置
比如 (?=i) ,表示 字符 ‘i’ 前面的位置
var result = 'hello'.replace(/(?=l)/g,'#');
console.log(result);
// he#l#lo
而 (?!p) 是 (?=p) 的反面,比如
var result = 'hello'.replace(/(?!l)/g,'#');
console.log(result);
// #h#ell#o#
二者的学名: 正向先行断言 (positive lookahead) 和 负向先行断言( negative lookahead)
es6 中还支持: positive lookbehind 和 negative lookbehind
具体是: (?<=p) 和(?<!p)
也有的书上解释成环视,看看左边,看看右边
3. 位置的特性
对于位置可以理解为 空字符 ''
比如:
// hello 字符 等价于
// 'hello'
'h'+ ''+ 'l'+ ''+ 'l'+ '' + 'l' + 'o'
所以说如下写法是没有问题的
var result = /^^hello$$$$/.test('hello')
console.log(result);
// true
var result2 = /(?=he)^^he(?=\w)llo$\b\b$/.test('hello')
console.log(result2);
// true
也就是说 字符之间的位置可以写成多个(位置是空字符串,随便叠加没问题)
4. 相关案例
4.1 不匹配任何东西的正则
/.^/
4.2 数字千位分割表示法
比如 ‘12345678’ 变为 ‘12,345,678’
4.2.1 弄出最后一个逗号
var regexp = /(?=\d{3}$)/g;
var result = '12345678'.replace(regexp, ',');
console.log(result);
// 12345,678
4.2.2 弄出所有逗号
var regexp = /(?=(\d{3})+$)/g;
var result = '12345678'.replace(regexp,',');
console.log(result);
// 12,345,678
4.2.3 验证案例
var result = '123456789'.replace(/(?=(\d{3})+$)/g,',')
console.log(result);
// ,123,456,789
// 修改后
var result = '123456789'.replace(/(?!^)(?=(\d{3})+$)/g,',')
console.log(result);
// 123,456,789
4.2.4 支持其他形式
将 '12345678 123456789'
替换成 '12,345,678 123,456,789'
var regexp = /(?!\b)(?=(\d{3})+\b)/g;
var result = '12345678 123456789'.replace(regexp, ',');
console.log(result);
// 12,345,678 123,456,789
其中 (?!\b) 可以理解为: 要求当前是一个位置,但不是 \b 前面的位置, 其实说的就是 \B
最终: /\B(?=(\d{3})+\b)/g
4.3 验证密码
要求: 密码长度 6-12, 有数字, 小写字符, 大写字母组成,但是必须至少是包含两种字符
分析:
- 密码长度: x{6,12}
- \d , [a-Z]
- \d a , \d A , a A , \d a A
分析没错,写法错了,
[\da-z]{6,12}
怎么保证 \d 被选中
var regexp = /^([\da-z]{6,12}|[\dA-Z]{6,12}|[A-z]{6,12}|[A-z\d]{6,12})$/g
4.3.1
不考虑至少 包含两种字符这一条件:var reg = /^[\dA-z]{6,12}$/
4.3.2 判断是否包含某一种字符
我们可以使用 (?=.*[0-9])
来要求必须包含数字
正则变为: var reg = /(?=.*[0-9])^[\dA-z]{6,12}$/
这个体现了 锚字符随便放,0-9字符前面是任意字符,所以 0-9 必须含有
4.3.3 同时包含具体两种字符
比如同时包含数字和小写字母, 可以用 (?=.*[0-9])(?=.*[a-z])
正则变成: var reg = /(?=.*[0-9])(?=.*[a-z])^[\dA-z]{6,12}$/
4.3.4 解答
原题可以等价变换为:
- 同时包含数字和小写字母
- 同时包含数字和大写字母
- 同时包含小写字母和大写字母
- 同时包含数字,小写字母,大写字母
以上四种情况是或的关系(实际上可以不用第4条)
// 首先这是错误写法
var reg = /((?=.*[0-9])(?=.*[a-z])|(?=.*[0-9])(?=.*[A-Z])|(?=.*[A-z]))^[\dA-z]$/;
// 这是正确写法
var reg = /((?=.*[0-9])(?=.*[a-z])|(?=.*[0-9])(?=.*[A-Z])|(?=.*[a-z])(?=.*[A-Z]))^[0-9A-Za-z]{6,12}$/
// 这样缩写也没错
var reg = /((?=.*[0-9])(?=.*[a-z])|(?=.*[0-9])(?=.*[A-Z])|(?=.*[a-z])(?=.*[A-Z]))^[\dA-z]{6,12}$/
本题中有一种写法 需要注意下
(?=.*[A-z])
和(?=.*[a-z])(?=.*[A-Z])
是不同的写法
(?=.*[A-z])
表示 必须包含 A-z 之一
(?=.*[a-z])(?=.*[A-Z])
表示 大小写同时包含
4.3.5 解惑
对于这个正则 :/(?=.*[0-9])^[\dA-z])$/
,只需要明白 (?=.*\d)^
即可。
分开来看就是 (?=.*[0-9]) 和 ^, 表示开头前面还有位置(当然也是开头,即同一个位置,类比空字符)
(?=.*[0-9])
表示该位置后面的字符匹配 .*[0-9],即有任何多个任意字符后面跟随数字(这里表示匹配子串的前面位置,也可以理解为这个位置后面是匹配子串),该位置后面的字符必须包含个数字
##### 4.3.6 另一种解法
“至少包含两种字符”意思就是说,不能全部是数字,不能全部是小写字母,不能全部是大写字母, 使用 (?!p)
要求不能全是 数字:var reg = /(?!^[0-9]{6,12}$)^[0-9A-Za-z]{6,12}$/;
最终答案:
var reg = /((?!^[0-9]{6,12}$)|(?!^[A-Z]{6,12})|(?!^[a-z]{6,12}))^[0-9A-Za-z]{6,12}$/;
reg.test('AEFSD123123');