贪婪模式跟惰性模式的区别
贪婪匹配是先看整个字符串是否匹配,如果不匹配,它会去掉字符串的最后一个字符,并再次尝试,如果还不匹配,那么再去掉当前最后一个,直到发现匹配或不剩任何字符。
惰性匹配是从左侧第一个字符开始向右匹配, 先看第一个字符是不是一个匹配, 如果不匹配就加入下一个字符再尝式匹配, 直到发现匹配。
| 模式 | 说明 |
|---|---|
| {n,m} | 连续出现 n 到 m 次。贪婪模式 |
| {n,} | 至少连续出现 n 次。贪婪模式 |
| {n} | 连续出现 n 次。贪婪模式 |
| ? | 等价于 {0,1}。贪婪模式 |
| + | 等价于 {1,}。贪婪模式 |
| _ | 等价于 {0,}。贪婪模式 |
量词后面加上问号后如下:
| 模式 | 说明 |
|---|---|
| {n,m}? | 连续出现 n 到 m 次。惰性模式 |
| {n,}? | 至少连续出现 n 次。惰性模式 |
| {n}? | 连续出现 n 次。惰性模式 |
| ?? | 等价于 {0,1}?。惰性模式 |
| +? | 等价于 {1,}?。惰性模式 |
| _? | 等价于 {0,}?。惰性模式 |
回溯
正则表达式匹配目标字符串时,它从左到右逐个测试表达式的组成部分,看是否能找到匹配项。在遇到量词时,需要决定何时尝试匹配更多字符。在遇到分支时,必须从可选项中选择一个尝试匹配。每当正则做类似的决定时,如果有必要,都会记录其他选择,以便匹配不成功时进行回溯,到最后一个决策点,再重新进行匹配。回溯失控时会导致匹配过慢。
断言
包括正先行断言(前瞻)(?=X)、负先行断言(前瞻)(?!X)、正后发断言(后顾)(?<=X)、负后发断言(后顾)(?<!X),断言简单来说是一个
匹配条件而不参与匹配内容。
| 语法 | 说明 |
|---|---|
| (?=X) | 正先行断言。仅当子表达式 X 在此位置的右侧匹配时才继续匹配。例如,/w+(?=\d)/ 与后面跟数字的单词匹配,而不与该数字匹配,此构造不会回溯 |
| (?!X) | 负先行断言。仅当子表达式 X 不在此位置的右侧匹配时才继续匹配。例如,/w+(?!\d)/ 与后面不跟数字的单词匹配,而不与该数字匹配 |
| (?<=X) | 正后发断言。仅当子表达式 X 在此位置的左侧匹配时才继续匹配。例如,/(?<=19)99/ 与跟在 19 后面的 99 的实例匹配,此构造不会回溯 |
| (?<!X) | 负后发断言。仅当子表达式 X 不在此位置的左侧匹配时才继续匹配。例如,/(?<!19)99/ 与不跟在 19 后面的 99 的实例匹配 |
'yx999'.replace(/x(?=(\d{3}))/g, 'P'); // yP999
'y999x'.replace(/(?!(\d{3}))x/g, 'P'); // y999P
'y999x'.replace(/(?<=(\d{3}))x/g, 'P'); // y999P
'yx999'.replace(/x(?<!(\d{3}))/g, 'P'); // yP999
捕获分组
- 正常只使用括号就是捕获模式,可以捕获括号里的数据,保存在内存中,会占用更多内存
- 非捕获模式,就是在里面的最前面加
?:,RegExp 不会保存该分组的数据
let regex1 = /(\d{4})-(\d{1,2})-(\d{2})/;
regex1.test('2030-10-1'); // true
RegExp.$1; // "2030"
RegExp.$2; // "10"
RegExp.$3; // "1"
let regex2 = /(?:\d{4})-(\d{1,2})-(?:\d{2})/;
regex2.test('2030-10-1'); // true
RegExp.$1; // "10"
RegExp.$2; // ""
边界
正则表达式中:
\b表示单词边界,\B表示非单词边界,应理解为(非单词)边界,而不是非(单词边界),它仍然匹配的是边界。
正则中所说的单词指的是\w可以匹配的字符,即数字、大小写字母以及下划线[0-9a-zA-Z_]。
将正则中的位置分为字符的占位和字符的间隙。字符的占位是显式的位置。
字符的间隙是隐式的位置。而边界指的是占位的字符左右的间隙位置。
\b 单词边界
\b是正则表达式规定的一个特殊代码,代表着单词的开头或结尾,也就是单词的分界处。虽然通常英文的单词是由空格,标点符号或者换行来分隔的,但是 \b 并不匹配这些单词分隔字符中的任何一个,它只匹配一个位置。
\b匹配这样的位置:它的前一个字符和后一个字符不全是(一个是,一个不是或不存在)\w。
// 只有首尾位置匹配
console.log('0aZ_'.replace(/\b/g, '.')); // .0aZ_.
// +不是\w,所以它的左右间隙都可以被匹配
console.log('a+a'.replace(/\b/g, '.')); // .a.+.a.
// 空格也不是\w,所以它的左右间隙都可以被匹配
console.log('a a'.replace(/\b/g, '.')); // .a. .a.
// 举例:
const str = "It's a nice day today";
// 正确的正则
console.log(str.replace(/\bnice\b/g, '.')); // "It's a . day today"
// 错误的正则
console.log(str.replace(/a\bnice/g, '.')); // "It's a nice day today"
// 纠正的正则
// '.'匹配除换行符(\n、\r)之外的任何单个字符, '\s'匹配一切空白符,或者直接使用空格' '也可
console.log(str.replace(/a\b.\bnice/g, '.')); // "It's . day today"
\B 非单词边界
\B匹配的也是边界 ,针对的是与\b相反的非单词\W,即所有不能被\b匹配的边界。
console.log('0aZ_'.replace(/\B/g, '.')); // 0.a.Z._
console.log('a+a'.replace(/\B/g, '.')); // a+a
console.log('a a'.replace(/\B/g, '.')); // a a
// 价格匹配
// (?=(\d{3})+(?!\d)) 声明匹配一个或多个三位数的组,且它们右侧为非数字,不理解的话请回顾上面的断言
'18888.99'.replace(/\B(?=(\d{3})+(?!\d))/g, ','); // '18,888.99';