正则表达式中的?:、?=和?!有什么区别

要区分?:、?=和?!三者,首先说说它们三者分别的作用是什么。

1. ?= 为前瞻匹配,exp1(?=exp2) 查找exp2前面的exp1。先来一个例子:

// 随便打了一串字符串
let str = '123sertersdTYgfgbh45t6343Dfdgbgdfher345#$%FTGH4t34dfg34t56G8rtuy56fgbT6dfy58845er';
// 查找大小写字母前面的3-5位的数字并替换为 -$-
let res = str.replace(/\d{3,5}(?=[a-zA-Z])/g, '-$-');
console.log(str);
console.log(res);
复制代码

打印值为:

123sertersdTYgfgbh45t6343Dfdgbgdfher345#$%FTGH4t34dfg34t56G8rtuy56fgbT6dfy58845er
-$-sertersdTYgfgbh45t-$-Dfdgbgdfher345#$%FTGH4t34dfg34t56G8rtuy56fgbT6dfy-$-er
复制代码

2. ?! 为负前瞻匹配,exp1(?!exp2) 查找后面不为exp2的exp1。来一个例子:

let str = '123sertersdTYgfgbh45t6343Dfdgbgdfher345#$%FTGH4t34dfg34t56G8rtuy56fgbT6dfy58845er';
// 查找后面不为字母的3-5位的数字并替换为 -$-
let res_ = str.replace(/\d{3,5}(?![a-zA-Z])/g, '-$-');
console.log(str);
console.log(res_);
复制代码

打印值为:

123sertersdTYgfgbh45t6343Dfdgbgdfher345#$%FTGH4t34dfg34t56G8rtuy56fgbT6dfy58845er
123sertersdTYgfgbh45t-$-3Dfdgbgdfher-$-#$%FTGH4t34dfg34t56G8rtuy56fgbT6dfy-$-5er
复制代码

3. ?<= 为后顾匹配,(?<=exp1)exp2 查找exp1后面的exp2。再来一个例子:

let str = '123sertersdTYgfgbh45t6343Dfdgbgdfher345#$%FTGH4t34dfg34t56G8rtuy56fgbT6dfy58845er';
// 查找3-5位的数字后面的连续字母并替换为 -$-
let res_b = str.replace(/(?<=\d{3,5})[a-zA-Z]+/g, '-$-');
console.log(str);
console.log(res_b);
复制代码

打印值为:

123sertersdTYgfgbh45t6343Dfdgbgdfher345#$%FTGH4t34dfg34t56G8rtuy56fgbT6dfy58845er
123-$-45t6343-$-345#$%FTGH4t34dfg34t56G8rtuy56fgbT6dfy58845-$-
复制代码

4. ?<! 为负后顾匹配,(?<!exp1)exp2 查找前面不是exp1的exp2。再来一个例子:

let str = '123sertersdTYgfgbh45t6343Dfdgbgdfher345#$%FTGH4t34dfg34t56G8rtuy56fgbT6dfy58845er';
// 查找前面不是3-5位的数字的连续字母并替换为 -$-
let res_nb = str.replace(/(?<!\d{3,5})[a-zA-Z]+/g, '-$-');
console.log(str);
console.log(res_nb);
复制代码

打印值为:

123sertersdTYgfgbh45t6343Dfdgbgdfher345#$%FTGH4t34dfg34t56G8rtuy56fgbT6dfy58845er
123s-$-45-$-6343D-$-345#$%-$-4-$-34-$-34-$-56-$-8-$-56-$-6-$-58845e-$-
复制代码

5. ?:

最后是?:,它为非捕获分组 (?:),说到非捕获分组就得先说一说捕获分组 ( ) ,捕获分组 ( ) 会把每个分组里匹配的值保存起来,使用$n(n是一个数字,表示第n个捕获组的内容)。非捕获分组和捕获分组唯一的区别在于,非捕获分组匹配的值不会保存起来。

非捕获分组和捕获分组的例子如下:

// 在本例中,我们将把第一个捕获分组和第二个捕获分组的内容交换, "Doe, John" 转换为 "John Doe"
let n = "Doe   ,  John";
let nn = n.replace(/(\w+)\s*,\s*(\w+)/, "$2 $1");
console.log(nn); // John Doe

// 改为非捕获分组
// 此处只有一个捕获分组,(?:\w+)只匹配并没有存储值,$2实际是不存在的,被视为一个字符串$2
let nbn = n.replace(/(\w+)\s*,\s*(?:\w+)/, "$2 $1");
console.log(nbn); // $2 Doe
复制代码

最后再来一个千位分割符的例子

let q = "1234567890".replace(/\B(?=(?:\d{3})+(?!\d))/g,",");
console.log(q); // 1,234,567,890
复制代码

上面已经把前瞻匹配(?=)、非捕获分组(?:)和负前瞻匹配(?!)介绍过了,这里具体分析一下这仨在千位分割符例子中的应用。

先看/\B(?=)/g这一部分,它是前瞻匹配,全局查询符合?=后面条件的\B(匹配不是单词开头或结束的位置),然后替换为,。再看?=后面的部分(?:\d{3})+(?!\d)(?:\d{3})是一个匹配3个数字的非捕获分组,后面的+表示前面的可以匹配3n个数字的非捕获分组;(?!\d)是负前瞻匹配,组合前面的(?:\d{3})+即为匹配后面不是数字的3n个数字的非捕获分组。

综上所述: 翻译翻译这个正则表达式即是查询后面能匹配3n个数字加一个非数字 的非单词边界。

1 2 3 4 5 6 7 8 9 0
1,2 3 4|5 6 7|8 9 0 (此处是单词边界,不为数字,且前面为3n个数字),匹配成功
1,2,3 4 5|6 7 8|9 0 (不满足3n个数字的匹配条件),匹配失败,尝试下一种情况
1,2 3,4 5 6|7 8 9|0 (不满足3n个数字的匹配条件),匹配失败,尝试下一种情况
1,2 3 4,5 6 7|8 9 0 (此处是单词边界,不为数字,且前面为3n个数字),匹配成功
1,2 3 4,5,6 7 8|9 0 (不满足3n个数字的匹配条件),匹配失败,尝试下一种情况
1,2 3 4,5 6,7 8 9|0 (不满足3n个数字的匹配条件),匹配失败,尝试下一种情况
1,2 3 4,5 6 7,8 9 0 (此处是单词边界,不为数字,且前面为3n个数字),匹配成功
1,2 3 4,5 6 7,8,9 0 (不满足3n个数字的匹配条件),匹配失败,尝试下一种情况
1,2 3 4,5 6 7,8 9,0 (不满足3n个数字的匹配条件),匹配失败,尝试下一种情况
1,2 3 4,5 6 7,8 9 0|(不满足非单词边界的匹配条件),匹配失败,尝试结束

最终结果为:
1,2 3 4,5 6 7,8 9 0
复制代码
© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享