👩🏻🌾 位置匹配攻略
正则表达式是匹配模式,要么匹配字符,要么匹配位置。
1. 什么是位置呢?
位置(锚)是相邻字符之间的位置,包括开头和结尾。
2. 如何匹配位置呢?
在es5中,共有6个锚:^、$、\b、\B、(?=p)、(?!p)。
还有两个es2018中的(?<=p) 和 (?<!p)
相应的可视化形式为:
位置锚:^ 和 $
- ^(脱字符):匹配开头,在多行匹配中匹配行开头。
- $(美元符号):匹配结尾,在多行匹配中匹配行结尾。
示例:
把字符串的开头和结尾用#替换。(位置可以替换成字符串的!)
const result = 'hello'.replace(/^|$/g,'#')
console.log(result);
//=>"#hello#"
多行匹配模式(即有修饰符m时),二者是行的概念。
const result = "I\nlove\njavascript".replace(/^|$/m,'#')
console.log(result);
//=>
#I#
#love#
#javescript#
位置锚:\b 和 \B
- \b:单词边界。具体就是:\w和\W之间的位置、\w和^之间的位置、\w和$之间的位置。
- \B:非单词边界。具体就是:\w和\w、\W和\W、\W和^、\W和$
\w是字符组[0-9a-zA-Z]的简写形式,而\W是排除字符组[^0-9a-zA-Z]的简写形式。
示例:
(1)考察文件名"[JS] Lesson_01.mp4"中的\b。
const result = "[JS] Lesson_01.mp4".replace(/\b/g,"#")
console.log(result);
//=>"[#JS#] #Lesson_01#.#mp4#"
上例解析:
输出结果"[#JS#]#Lesson_01#.#mp4#"中的每一个#号:
- 第1个:两边字符是 [ 和 J ,是\W和\w之间的位置。
- 第2个:两边字符是 S 和 ] ,是\w和\W之间的位置。
- 第3个:两边字符是 ] 和 L ,是\W和\w之间的位置。
- 第4个:两边字符是 1 和 . ,是\w和\W之间的位置。
- 第5个:两边字符是 . 和 m ,是\W和\w之间的位置。
- 第6个:位于结尾,前面字符4是\w ,即\w和$之间的位置。
(2)文件名"[JS] Lesson_01.mp4"中的\B。
const result = "[JS] Lesson_01.mp4".replace(/\B/g,"#")
console.log(result);
//=>"#[J#S]# L#e#s#s#o#n#_#0#1.m#p#4" // \W和^、\w和\w、\W和\W
位置锚:(?=p) 和 (?!p)
这是两个先行断言。
- (?=p):正向先行断言。其中p是一个
子表达式,即p前面的位置,或者说该位置后面的字符匹配p。 - (?!p):负向先行断言。(?=p)的反面意思。
const result = "hello".replace(/(?=l)/g,'#');
console.log(result);
//=> "he#l#lo"
const result = "hello".replace(/(?=ll)/g,'#');
console.log(result);
//=> "he#llo"
上例中:(?=l)就表示“l”字符前面的那个位置。(?=ll)表示“ll”字符前面的位置
可视化图为:
const result = "hello".replace(/(?!l)/g,"#");
console.log(result);
//=> "#h#ell#o#"
上例中:(?!l)表示不是“l”字符前面的位置,也包括开头和结尾。
位置锚:(?<=p) 和 (?<!p)
这是两个后行断言。
- (?<=p):正向后行断言。表示p表达式后面那个
位置。 - (?<!p):负向后行断言。与(?<=p)相反,表示不能匹配p表达式后面的
位置
const result = "hello".replace(/(?<=l)/g,"#");
console.log(result);
//=> "hel#l#o"
const result = "hello".replace(/(?<!l)/g,"#");
console.log(result);
//=> "#h#e#llo#"
其中/(?<=l)/表示l后面的那个位置,/(?<!l)/表示不是l后面的位置。
es5 就支持了先行断言。 es2018才支持后行断言
有些资料没有很好的强调(?=p)、(?!p)、(?<=p)和(?<!p)这四者就是个位置。比如(?=p),就与开头^一样,就是p前面那个位置。
零宽断言:
零宽:只匹配位置,在匹配过程中,不占用字符,所以被称为零宽。
(?=p)、(?!p)、(?<=p)和(?<!p)统称为零宽断言,也可以称之为环视,即左看看右看看。
3. 位置的特性
对于位置的理解,可以理解成空字符串“”。字符之间的位置可以写成多个。
位置理解成空字符,是对位置非常有效的理解方式。
比如"hello"字符串等价于如下的形式:
"hello" == ""+"h"+""+"e"+""+"l"+""+"l"+""+"o"+"";
"hello" == ""+""+"hello";
因此:/^hello$/ == /^^hello$$$/
const result = /^^hello$$$/.test("hello");
console.log(result);
//=>true
更复杂的例子:
const result = /(?=he)^^he(?=\w)llo$\b\b$/.test("hello");
console.log(result);
//=>true
4. 案例
不匹配任何东西的正则
/.^/ : 要求.字符后面是字符串开头,而这样的字符串是不存在的。
数据的千位分隔符表示法
- 题1:将“12345678”,变成“12,345,678”。
实现:将相应的位置替换成“,”。
const result = "12345678".replace(/(?!^)(?=(\d{3})+$)/g,',')
//=>"12,345,678"
其中(?!^)表示匹配的位置不能是开头。(?=(\d{3})+$)表示从结尾向前数,一旦是3的倍数,就把其前面的位置替换成逗号。
如果不加(?!^),则"123456789"会处理成",123,456,789",所以要把开头排除掉。
- 题2:把 "12345678 123456789" 替换成 "12,345,678 123,456,789"
这个应该是从结尾和数字之间位置、空格和数字之间的位置开始往前数,即\b。 不能在开头、空格和数字之间的位置,即\b。实现如下:
const result = "12345678 123456789".replace(/(?!\b)(?=(\d{3})+\b)/g,',')
//=>"12,345,678 123,456,789"
其中(?!\b)其实就是\B,所以最终的正则是:/\B(?=(\d{3})+\b)/g。
其可视化图为:
验证密码问题
题:密码长度 6-12 位,由数字、小写字符和大写字母组成,但必须至少包括 2 种字符。
解法1(推荐):
“至少包含2种字符”,那么不能全部都是数字,也不能全部都是小写字母,也不能全部都是大写字母。
const regex = /(?!^[0-9]{6,12}$)(?!^[a-z]{6,12}$)(?!^[A-Z]{6,12}$)^[0-9a-zA-Z]{6,12}$/g;
console.log(regex.test('1234567')) // false,全是数字
console.log(regex.test('aaaaaa')) // false,全是小写字母
console.log(regex.test('AAAAAA')) // false,全是大写字母
console.log(regex.test('aaAA1')) // false,不是最小6位
其中:
- (?!^[0-9]{6,12}$):表示不能全部是数字。
- (?!^[a-z]{6,12}$):表示不能全部是小写字母。
- (?!^[A-Z]{6,12}$):表示不能全部是大写字母。 其可视化形式为:
解法2:
同时包含两种字符的情况有:
- 同时包含数字和小写字母
- 同时包含数字和大写字母
- 同时包含小写字母和大写字母
const regex = /((?=.*[0-9])(?=.*[a-z])|(?=.*[0-9])(?=.*[A-Z])|(?=.*[a-z])(?=.*[A-Z]))^[0-9a-zA-Z]{6,12}$/g;
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 三者都有
其中: (?=.[0-9])表示必须包含数字,(?=.[0-9])(?=.*[a-z])表示必须包含数字和字母,其他同理。
其可视化过程为: