Java充电社
专辑
博文
联系我
本人继续续收门徒,亲手指导
正则表达式专题(JS版)
-> 正则表达式位置匹配攻略
1、JS正则表达式完整教程
2、正则表达式字符匹配攻略
3、正则表达式位置匹配攻略
4、正则表达式括号的作用
5、正则表达式回溯法原理
6、正则表达式的拆分
7、正则表达式的构建
8、正则表达式编程
9、下载PDF版
上一篇:正则表达式字符匹配攻略
下一篇:正则表达式括号的作用
<div style="display:none"></div> 正则表达式是匹配模式,要么匹配字符,要么匹配位置。请记住这句话。 然而大部分人学习正则时,对于匹配位置的重视程度没有那么高。 本章讲讲正则匹配位置的总总。 ## 内容包括 1. 什么是位置? 2. 如何匹配位置? 3. 位置的特性 4. 几个应用实例分析 ## 1. 什么是位置呢? 位置是相邻字符之间的位置。比如,下图中箭头所指的地方: ![](https://itsoku.oss-cn-hangzhou.aliyuncs.com/itsoku/blog/article/172/2f265453-15fb-43ca-b8c2-7f27ab20b0cd.png) ## 2. 如何匹配位置呢? 在ES5中,共有6个锚字符: > `^ $ \b \B (?=p) (?!p)` ### 2.1 ^和$ `^`(脱字符)匹配开头,在多行匹配中匹配行开头。 `$`(美元符号)匹配结尾,在多行匹配中匹配行结尾。 比如我们把字符串的开头和结尾用"#"替换(位置可以替换成字符的!): ```javascript var result = "hello".replace(/^|$/g, '#'); console.log(result); // => "#hello#" ``` 多行匹配模式时,二者是行的概念,这个需要我们的注意: ```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`,如下: ```javascript var result = "[JS] Lesson_01.mp4".replace(/\b/g, '#'); console.log(result); // => "[#JS#] #Lesson_01#.#mp4#" ``` 为什么是这样呢?这需要仔细看看。 首先,我们知道,`\w`是字符组`[0-9a-zA-Z_]`的简写形式,即`\w`是字母数字或者下划线的中任何一个字符。而`\W`是排除字符组`[^0-9a-zA-Z_]`的简写形式,即`\W`是`\w`以外的任何一个字符。 此时我们可以看看"[#JS#] #Lesson_01#.#mp4#"中的每一个"#",是怎么来的。 - 第一个"#",两边是"["与"J",是`\W`和`\w`之间的位置。 - 第二个"#",两边是"S"与"]",也就是`\w`和`\W`之间的位置。 - 第三个"#",两边是空格与"L",也就是`\W`和`\w`之间的位置。 - 第四个"#",两边是"1"与".",也就是`\w`和`\W`之间的位置。 - 第五个"#",两边是"."与"m",也就是`\W`和`\w`之间的位置。 - 第六个"#",其对应的位置是结尾,但其前面的字符"4"是`\w`,即`\w`和`$`之间的位置。 知道了`\b`的概念后,那么`\B`也就相对好理解了。 `\B`就是`\b`的反面的意思,非单词边界。例如在字符串中所有位置中,扣掉`\b`,剩下的都是`\B`的。 具体说来就是`\w`与`\w`、`\W`与`\W`、`^`与`\W`,`\W`与`$`之间的位置。 比如上面的例子,把所有`\B`替换成"#": ```javascript 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`前面的位置。 比如`(?=l)`,表示'l'字符前面的位置,例如: ```javascript var result = "hello".replace(/(?=l)/g, '#'); console.log(result); // => "he#l#lo" ``` 而`(?!p)`就是`(?=p)`的反面意思,比如: ```javascript var result = "hello".replace(/(?!l)/g, '#'); console.log(result); // => "#h#ell#o#" ``` 二者的学名分别是positive lookahead和negative lookahead。 中文翻译分别是正向先行断言和负向先行断言。 ES6中,还支持positive lookbehind和negative lookbehind。 具体是`(?<=p)`和`(?<!p)`。 也有书上把这四个东西,翻译成环视,即看看右边或看看左边。 但一般书上,没有很好强调这四者是个位置。 比如`(?=p)`,一般都理解成:要求接下来的字符与`p`匹配,但不能包括`p`的那些字符。 而在本人看来`(?=p)`就与`^`一样好理解,就是`p`前面的那个位置。 ## 3. 位置的特性 对于位置的理解,我们可以理解成空字符""。 比如"hello"字符串等价于如下的形式: ```javascript "hello" == "" + "h" + "" + "e" + "" + "l" + "" + "l" + "o" + ""; ``` 也等价于: ```javascript "hello" == "" + "" + "hello" ``` 因此,把`/^hello$/`写成`/^^hello$$$/`,是没有任何问题的: ```javascript var result = /^^hello$$$/.test("hello"); console.log(result); // => true ``` 甚至可以写成更复杂的: ```javascript var result = /(?=he)^^he(?=\w)llo$\b\b$/.test("hello"); console.log(result); // => true ``` 也就是说字符之间的位置,可以写成多个。 把位置理解空字符,是对位置非常有效的理解方式。 ## 4. 相关案例 ### 4.1 不匹配任何东西的正则 让你写个正则不匹配任何东西 easy,`/.^/` 因为此正则要求只有一个字符,但该字符后面是开头。 ### 4.2 数字的千位分隔符表示法 比如把"12345678",变成"12,345,678"。 可见是需要把相应的位置替换成","。 思路是什么呢? #### 4.2.1 弄出最后一个逗号 使用`(?=\d{3}$)`就可以做到: ```javascript var result = "12345678".replace(/(?=\d{3}$)/g, ',') console.log(result); // => "12345,678" ``` #### 4.2.2 弄出所有的逗号 因为逗号出现的位置,要求后面3个数字一组,也就是`\d{3}`至少出现一次。 此时可以使用量词`+`: ```javascript var result = "12345678".replace(/(?=(\d{3})+$)/g, ',') console.log(result); // => "12,345,678" ``` #### 4.2.3 匹配其余案例 写完正则后,要多验证几个案例,此时我们会发现问题: ```javascript var result = "123456789".replace(/(?=(\d{3})+$)/g, ',') console.log(result); // => ",123,456,789" ``` 因为上面的正则,仅仅表示把从结尾向前数,一但是3的倍数,就把其前面的位置替换成逗号。因此才会出现这个问题。 怎么解决呢?我们要求匹配的到这个位置不能是开头。 我们知道匹配开头可以使用`^`,但要求这个位置不是开头怎么办? easy,`(?!^)`,你想到了吗?测试如下: ```javascript var string1 = "12345678", string2 = "123456789"; reg = /(?!^)(?=(\d{3})+$)/g; var result = string1.replace(reg, ',') console.log(result); // => "12,345,678" result = string2.replace(reg, ','); console.log(result); // => "123,456,789" ``` #### 4.2.4 支持其他形式 如果要把"12345678 123456789"替换成"12,345,678 123,456,789"。 此时我们需要修改正则,把里面的开头`^`和结尾`$`,替换成`\b`: ```javascript var string = "12345678 123456789", reg = /(?!\b)(?=(\d{3})+\b)/g; var result = string.replace(reg, ',') console.log(result); // => "12,345,678 123,456,789" ``` 其中`(?!\b)`怎么理解呢? 要求当前是一个位置,但不是`\b`前面的位置,其实`(?!\b)`说的就是`\B`。 因此最终正则变成了:`/\B(?=(\d{3})+\b)/g`。 ### 4.3 验证密码问题 密码长度6-12位,由数字、小写字符和大写字母组成,但必须至少包括2种字符。 此题,如果写成多个正则来判断,比较容易。但要写成一个正则就比较困难。 那么,我们就来挑战一下。看看我们对位置的理解是否深刻。 #### 4.3.1 简化 不考虑“但必须至少包括2种字符”这一条件。我们可以容易写出: ```javascript var reg = /^[0-9A-Za-z]{6,12}$/; ``` #### 4.3.2 判断是否包含有某一种字符 假设,要求的必须包含数字,怎么办?此时我们可以使用`(?=.*[0-9])`来做。 因此正则变成: ```javascript var reg = /(?=.*[0-9])^[0-9A-Za-z]{6,12}$/; ``` #### 4.3.3 同时包含具体两种字符 比如同时包含数字和小写字母,可以用`(?=.*[0-9])(?=.*[a-z])`来做。 因此正则变成: ```javascript var reg = /(?=.*[0-9])(?=.*[a-z])^[0-9A-Za-z]{6,12}$/; ``` #### 4.3.4 解答 我们可以把原题变成下列几种情况之一: 1. 同时包含数字和小写字母 2. 同时包含数字和大写字母 3. 同时包含小写字母和大写字母 4. 同时包含数字、小写字母和大写字母 以上的4种情况是或的关系(实际上,可以不用第4条)。 最终答案是: ```javascript var reg = /((?=.*[0-9])(?=.*[a-z])|(?=.*[0-9])(?=.*[A-Z])|(?=.*[a-z])(?=.*[A-Z]))^[0-9A-Za-z]{6,12}$/; console.log( reg.test("1234567") ); // false 全是数字 console.log( reg.test("abcdef") ); // false 全是小写字母 console.log( reg.test("ABCDEFGH") ); // false 全是大写字母 console.log( reg.test("ab23C") ); // false 不足6位 console.log( reg.test("ABCDEF234") ); // true 大写字母和数字 console.log( reg.test("abcdEF234") ); // true 三者都有 ``` #### 4.3.5 解惑 上面的正则看起来比较复杂,只要理解了第二步,其余就全部理解了。 ``` /(?=.*[0-9])^[0-9A-Za-z]{6,12}$/ ``` 对于这个正则,我们只需要弄明白`(?=.*[0-9])^`即可。 分开来看就是`(?=.*[0-9])`和`^`。 表示开头前面还有个位置(当然也是开头,即同一个位置,想想之前的空字符类比)。 `(?=.*[0-9])`表示该位置后面的字符匹配`.*[0-9]`,即,有任何多个任意字符,后面再跟个数字。 翻译成大白话,就是接下来的字符,必须包含个数字。 #### 4.3.6 另外一种解法 “至少包含两种字符”的意思就是说,不能全部都是数字,也不能全部都是小写字母,也不能全部都是大写字母。 那么要求“不能全部都是数字”,怎么做呢?`(?!p)`出马! 对应的正则是: ```javascript var reg = /(?!^[0-9]{6,12}$)^[0-9A-Za-z]{6,12}$/; ``` 三种“都不能”呢? 最终答案是: ```javascript var reg = /(?!^[0-9]{6,12}$)(?!^[a-z]{6,12}$)(?!^[A-Z]{6,12}$)^[0-9A-Za-z]{6,12}$/; console.log( reg.test("1234567") ); // false 全是数字 console.log( reg.test("abcdef") ); // false 全是小写字母 console.log( reg.test("ABCDEFGH") ); // false 全是大写字母 console.log( reg.test("ab23C") ); // false 不足6位 console.log( reg.test("ABCDEF234") ); // true 大写字母和数字 console.log( reg.test("abcdEF234") ); // true 三者都有 ``` ## 小结 位置匹配相关的案例,挺多的,不一而足。 掌握匹配位置的这6个锚字符,给我们解决正则问题一个新工具。 ```html 作者:老姚 链接:https://juejin.im/post/5965943ff265da6c30653879 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。 ``` <a style="display:none" target="_blank" href="https://mp.weixin.qq.com/s/_S1DD2JADnXvpexxaBwLLg" style="color:red; font-size:20px; font-weight:bold">继续收门徒,亲手带,月薪 4W 以下的可以来找我</a> ## 最新资料 1. <a href="https://mp.weixin.qq.com/s?__biz=MzkzOTI3Nzc0Mg==&mid=2247484964&idx=2&sn=c81bce2f26015ee0f9632ddc6c67df03&scene=21#wechat_redirect" target="_blank">尚硅谷 Java 学科全套教程(总 207.77GB)</a> 2. <a href="https://mp.weixin.qq.com/s?__biz=MzkwOTAyMTY2NA==&mid=2247484192&idx=1&sn=505f2faaa4cc911f553850667749bcbb&scene=21#wechat_redirect" target="_blank">2021 最新版 Java 微服务学习线路图 + 视频</a> 3. <a href="https://mp.weixin.qq.com/s?__biz=MzkwOTAyMTY2NA==&mid=2247484573&idx=1&sn=7f3d83892186c16c57bc0b99f03f1ffd&scene=21#wechat_redirect" target="_blank">阿里技术大佬整理的《Spring 学习笔记.pdf》</a> 4. <a href="https://mp.weixin.qq.com/s?__biz=MzkwOTAyMTY2NA==&mid=2247484544&idx=2&sn=c1dfe907cfaa5b9ae8e66fc247ccbe84&scene=21#wechat_redirect" target="_blank">阿里大佬的《MySQL 学习笔记高清.pdf》</a> 5. <a href="https://mp.weixin.qq.com/s?__biz=MzkwOTAyMTY2NA==&mid=2247485167&idx=1&sn=48d75c8e93e748235a3547f34921dfb7&scene=21#wechat_redirect" target="_blank">2021 版 java 高并发常见面试题汇总.pdf</a> 6. <a href="https://mp.weixin.qq.com/s?__biz=MzkwOTAyMTY2NA==&mid=2247485664&idx=1&sn=435f9f515a8f881642820d7790ad20ce&scene=21#wechat_redirect" target="_blank">Idea 快捷键大全.pdf</a> ![](https://itsoku.oss-cn-hangzhou.aliyuncs.com/itsoku/blog/article/1/2883e86e-3eff-404a-8943-0066e5e2b454.png)
#custom-toc-container