Appearance
正则表达式
字符匹配 · 位置匹配 · 括号 · 回溯 · API
一、字符匹配基础
正则 = 匹配字符 或 匹配位置
1. 两种模糊匹配
- 横向模糊(量词):匹配长度不固定,如
{m,n}表示出现 m~n 次 - 纵向模糊(字符组):某一位可以是多种字符之一,如
[abc]
2. 字符组简写
| 简写 | 等价形式 | 含义 |
|---|---|---|
\d | [0-9] | 数字 |
\D | [^0-9] | 非数字 |
\w | [0-9a-zA-Z_] | 单词字符 |
\W | [^0-9a-zA-Z_] | 非单词字符 |
\s | [ \t\v\n\r\f] | 空白符 |
\S | [^ \t\v\n\r\f] | 非空白符 |
. | [^\n\r\u2028\u2029] | 通配符(几乎任意字符) |
- 匹配任意字符:
[\d\D]、[\w\W]、[\s\S]或[^]
3. 量词
| 量词 | 含义 |
|---|---|
{m,n} | 出现 m~n 次 |
{m,} | 至少 m 次 |
{m} | 恰好 m 次 |
? | {0,1} 出现 0 或 1 次 |
+ | {1,} 至少 1 次 |
* | {0,} 任意次 |
4. 贪婪 vs 惰性匹配
- 贪婪(默认):尽可能多匹配,如
\d{2,5}遇到 "123456" 会匹配 "12345" - 惰性(量词后加
?):尽可能少匹配,如\d{2,5}?遇到 "123456" 会匹配 "12" - 惰性量词:
{m,n}?{m,}???+?*?
5. 分支结构(也是惰性的)
(p1|p2|p3)中前面匹配成功后面不再尝试/good|goodbye/匹配 "goodbye" 只会得到 "good"- 应写为
/goodbye|good/(长的放前面)
二、位置匹配(6 个锚字符)
| 锚字符 | 含义 |
|---|---|
^ | 匹配开头(多行模式下匹配行开头) |
$ | 匹配结尾(多行模式下匹配行结尾) |
\b | 单词边界(\w 与 \W 之间) |
\B | 非单词边界 |
(?=p) | 正向先行断言(p 前面的位置) |
(?!p) | 负向先行断言(非 p 前面的位置) |
- ES6 还支持
(?<=p)(正向后行断言)和(?<!p)(负向后行断言) - 位置等价于空字符
"":"hello"等价于"" + "h" + "" + "e" + "" + ...
经典应用:
js
// 千位分隔符
"12345678".replace(/(?!^)(?=(\d{3})+$)/g, ',')
// => "12,345,678"
// 密码验证(至少包含两种字符,6-12位)
var reg = /(?!^[0-9]{6,12}$)(?!^[a-z]{6,12}$)(?!^[A-Z]{6,12}$)^[0-9A-Za-z]{6,12}$/三、括号的三大作用
- 分组:使量词作用于整体
/(ab)+/ - 捕获分组:提取数据 + 替换引用js
// 提取日期各部分 "2017-06-12".match(/(\d{4})-(\d{2})-(\d{2})/) // => ["2017-06-12", "2017", "06", "12"] // 替换引用分组 "2017-06-12".replace(/(\d{4})-(\d{2})-(\d{2})/, "$2/$3/$1") // => "06/12/2017" - 反向引用:
\1引用第一个分组匹配的内容js// 日期分隔符前后一致 /\d{4}(-|\/|\.)\d{2}\1\d{2}/.test("2017-06-12") // true /\d{4}(-|\/|\.)\d{2}\1\d{2}/.test("2017-06/12") // false
非捕获分组 (?:p):只用括号功能,不捕获数据
四、回溯法原理
- 正则引擎从左到右逐字符尝试匹配
- 遇到贪婪量词时先尽可能多匹配,如果后续匹配失败则逐步回退(回溯)
- 回溯是性能杀手,应避免不必要的回溯
js
// 低效(有回溯)
/id=".*?"/
// 高效(无回溯)
/id="[^"]*"/五、正则相关 API 要点
1. match 返回值因 g 修饰符不同
js
"2017.06.27".match(/\b(\d+)\b/)
// => ["2017", "2017", index: 0, input: "2017.06.27"] ← 有分组信息
"2017.06.27".match(/\b(\d+)\b/g)
// => ["2017", "06", "27"] ← 只有匹配结果,无分组和 index2. exec 比 match 更强(支持迭代匹配)
js
var regex = /\b(\d+)\b/g
var result
while (result = regex.exec("2017.06.27")) {
console.log(result[0], regex.lastIndex)
}
// 2017 4 → 06 7 → 27 103. 修饰符 g 对 test/exec 的影响
- 有
g时,每次匹配后lastIndex会更新,下次从新位置开始 - 陷阱:同一个带
g的正则多次test()结果可能不一致
js
var regex = /a/g
regex.test("a") // true, lastIndex=1
regex.test("aba") // true, lastIndex=3
regex.test("ababc") // false! lastIndex=0(从位置3开始找不到a)4. replace 的第二个参数
字符串形式:$1~$99 引用分组、$& 匹配的子串、$` 左边文本、$' 右边文本
函数形式:
js
"1234 2345".replace(/(\d)\d{2}(\d)/g, function(match, $1, $2, index, input) {
// match=完整匹配, $1/$2=分组, index=位置, input=原字符串
})5. 修饰符总结
| 修饰符 | 含义 |
|---|---|
g | 全局匹配 |
i | 忽略大小写 |
m | 多行模式(影响 ^ 和 $) |