正则表达式的主要作用,要么匹配模式,要么匹配位置, 要么匹配字符。
字符匹配攻略
模糊匹配: 横向 & 纵向
横向模糊匹配 (长度不固定)
横向模糊是指:长度不固定,可以是多种情况。
实现
使用量词,{m,n},连续出现最少m次,最多n次。
纵向模糊匹配 (某一位字符不确定,字符组实现)
纵向模糊是指:一个正则匹配的字符串,具体到某一位字符时,它可以不是某个特定的字符,可以是多种可能。
实现
使用字符组,eg [123]表示该字符可以是a、 b、c 任意一个字符
字符组 -- 只是其中字符之一
范围表示法
连字符 - 表示省略和简写 [a-z] :全部小写字符 [-az]、[az-]、[a-z]:表示a、-、z三者任意一个字符
排除字符组/反义字符组[^]
可以是任何东西,但特定几个除外 字符组第一位放^
常见简写形式
| 字符组 | 具体含义 |
|---|---|
| \d | [0-9],digit数字 |
| \D | [^0-9],除数字以外的任何字符 |
| \w | [0-9a-zA-Z],数字、大小写字母和下划线,word |
| \W | [^0-9a-zA-Z],非单词字符 |
| \s | [\t\v\n\r\f],空白符(空格、水平制表符、垂直制表符、换行符、回车符、换页符) |
| \S | [^\t\v\n\r\f],非空白符 |
| . | [^\n\r\u2028\u2029],通配符,占位符 |
量词
简写形式
| 量词 | 具体含义 |
|---|---|
| {m,} | 至少出现m 次 |
| {m} | 出现m次 |
| ? | { 0,1} |
| + | {1,} |
| * | {0 ,} |
贪婪匹配 & 惰性匹配
贪婪匹配:尽可能多匹配 惰性匹配: 尽可能少匹配 量词后面加个? 实现惰性匹配
| 惰性量词 | 贪婪量词 |
|---|---|
| {m,n}? | {m.n} |
| {m, }? | {m,} |
| ?? | ? |
| +? | + |
| *? | * |
多选分支(惰性匹配,多个子模式任选一)
多选分支:支持多个子模式,任选其一。
具体形式:(p1 | p2 | p3)
Eg: /good|nice ,匹配字符串good 或nice
多选分支是惰性匹配的,只要前面匹配上了,后面就不再匹配。 /good|goodbye 匹配goodbye 时只会匹配到good 就结束
练习
- 16进制颜色值匹配:
/#([0-9a-fA-F]{6}|[0-9a-fA-F]{3})/g - 匹配时间,24小时制:
/^([01][0-9]|[2][0-4]):[0-5][0-9]$/ - 匹配日期 yyyy-mm-dd格式
/^([0-9]{4}-(0[1-9]|1[1-2])-(0[1-9]|[12][0-9]|3[101]))$/ - window操作系统文件路径
- 模式: 盘符:\文件夹\文件夹
- 匹配id,从
<div id=“container” class=“main”></div>中匹配ID */id=".*?"/ VS /id="[^"]*"/
位置匹配攻略
位置是指:相邻字符之间的位置
| 锚 | 含义 | 举例 |
|---|---|---|
| ^ | 匹配开头 | |
| $ | 匹配结尾 | |
| \b | 单词边界,具体就是\w和\W之间的位置 | |
| \B | 非单词边界 | |
| (?=p) | 正向先行断言,p是一个子模式,p前面的位置,即接下来的字符与p匹配 | 'hello'.replace(/(?=l)/h,'#') => he#l#lo |
| (?!p) | 反向先行断言 | 'hello'.replace(/(?=l)/h,'#') => #h#ell#o |
| (?<=p) | 正向环视 | |
| (?<!p) | 反向环视 |
练习
1.数字的千位分隔符表示法
查看答案
从后往前数,且不是开头:num.replace(/ (?!^)(?=(\d{3})+$)/g,',')
2. 货币格式化 1888 -->$ 1888.00
查看答案
num.toFixed(2).replace(/\B(?=(\d{3})+\b)/g,',').replace(/^/,'$$ ');
- 校验密码,6-12位(数字+小写字符+大写字母),但必须包含2中字符
查看答案
+ 数字+小写 + 数字 + 大写 + 小写 + 大写 + 数字 + 小写 + 大写`reg = /((?=.*[0-9])(?=.*[a-z])|(?=.*[0-9])(?=.*[A-Z])|(?=.*[a-z])(?=.*))`
匹配原理
括号的作用- 分组 和 分支结构
1.分组
括号提供分组功能
Eg: /ab+/ 和 /(ab)+/
2.分组引用
正则引擎,在匹配过程中,给每个分组都开辟一个空间,用来存储每一个分组匹配到的数据
- 提取数据
- match,返回一个数据,第一个返回整体匹配结果,第二个返回各个分组匹配的内容,第三个返回匹配的下表,最后返回输入的文本
- 替换
- replace,第一个参数事正则表达式,第二个参数里用2 $3 指代相应分组;
3.反向引用 -- 正则本身引用分组
反向引用指 在正则本身里引用分组,但只能引用之前出现的分组
todo
Eg: /\d{4}(-|/|.)\d{2}\1\d{2}/
\1 表示引用之前的第1个分组(-|/|.)
思考:括号嵌套怎么办?
Eg:/^((\d)(\d(\d)))\1\2\3\4$/
\10 表示第10个分组
引用不存在的分组不会报错,会匹配反向引用的字符本身
比如 \2 就会匹配 "\2"
var regex = /\1\2\3\4\5\6\7\8\9/;
console.log( regex.test("\1\2\3\4\5\6\7\8\9") );
console.log( "\1\2\3\4\5\6\7\8\9".split("") );
分组后有量词,最终捕获的是 最后一次匹配的数据
var regex = /(\d)+/;
var string = "12345";
console.log( string.match(regex) );
// => ["12345", "5", index: 0, input: "12345"]
反向引用也是
var regex = /(\d)+ \1/;
console.log( regex.test("12345 1") );
// => false
console.log( regex.test("12345 5") );
// => true
4.非捕获括号 -- (?:p) (?:p1|p2|p3)
如果 ==既不在API里引用,也不在正则里反向引用==,只想要括号的最原始功能,并不会引用它,可以使用非捕获括号(?:p) 和 (?:p1|p2|p3)
练习
-
字符串trim方法模拟
查看答案
trim 去掉字符串开头和结尾的空符
str.replace('/^\s+|\s+$/g','')
-
每个单词首字母大写
查看答案
trim 去掉字符串开头和结尾的空符
`str.toLowerCase().replace('/(?:^|\s)\w/g', (c) => { return c.toUpperCase(); })`
-
驼峰化
查看答案
str.replace(/[-_\s]+(.)?/g, (c) => { return c ? c.toUpperCase() :''; }) -
驼峰 => 中划线
查看答案
str.replace(/([A-Z])/g, '-$1').replace(/[-_\s]+/g, '-').toLowerCase(); -
匹配成对标签 匹配
<title></title>不匹配<title></p>
反向引用:/<([^>]+)>[\d\D]*<\/\1>/
回溯法原理
reg = /ab{1,3}c/
str = 'abbbc'时,是**==没有回溯的匹配==**
str = 'abbc' 时,中间就有回溯
常见的回溯形式
回溯法:也称试探法,基本思想是 从问题的某一个状态(初始状态)出发,搜索从这种状态所能达到的所有状态,当一条路走到尽头不能再前进时,再后退一步或若干部,从另一种可能状态出发,继续搜索,直到所有的路径都试探过。这种不断前进回溯寻找解的方法,叫回溯法。
本质上就是深度优先搜索算法,其中推到之前的某一步的这个过程,我们称之为“回溯”。简单来说,就是路走不通时就会发生回溯。尝试匹配失败时,下一步通常就是回溯。
正则引擎
DFA:确定型有限自动机 NFA:确定型有限自动机.JavaScript 是NFA
正则表达式的拆分(读)
结构和操作符
结构:优先级 从上到下,由高到低,分支最低
| 结构 | 说明 |
|---|---|
| 字面量 | 匹配一个具体字符 |
| 字符组 | 匹配一个字符,可以是多种可能之一 |
| 量词 | 表示一个字符连续出现的次数 |
| 锚 | 匹配一个位置,而不是字符,比如 开头 和 结尾 |
| 分组 | 用()表示一个整体 ,Eg:(ab)+ VS (?:ab)+ |
| 分支 | 多个子表达式多选一 |
操作:优先级 从上到下,由高到低
| 操作符描述 | 操作符 |
|---|---|
| 转义符 | \ |
| 括号啊和方括号 | (...)、(?:...)、(?=...)、(?!...)、[...] |
| 量词限定符 | {m}、{m,n}、{m,}、?、*、+ |
| 位置和序列 | ^、$、\元字符、一般字符 |
| 管道符(竖杠) | | |
注意要点
- 量词连缀需要加括号
练习
1、匹配身份证
/^(\d{15}|\d{17}[\dxX])$/
2、IPV4地址
/^((0{0,2}\d|0?\d{2}|1\d{2}|2[0-4]\d|25[0-5])\.){3}(0{0,2}\d|0?\d{2}|1\d{2}|2[0-4]\d|25[0-5])$/
正则表达式的构建(写)
平衡法则
- 匹配预期的字符串
- 匹配非预期的字符串
- 可读性和可维护性
- 效率
构建正则前提
- 是否能使用正则?
- 是否有必要使用正则?能用字符串API解决的简繁问题就用api
- 是否有必要构建一个复杂的正则?
准确性
准确性,是能匹配预期的目标
- 了解各部分的模式规则
- 明确形式关系
效率
正则表达式运行过程:
- 编译
- 设定起始位置
- 尝试匹配
- 匹配失败,从下一位开始继续第3步
- 最终结果:匹配成功或失败
优化效率
- 使用具体型字符组来代替通配符,来消除回溯
- 使用非捕获型分组
- 独立出确定字符
- 提取分支公共部分
- 减少分支的数量,缩小它们的范围
相关API
字符串API
- search
- split
- match
- replace
正则API
- test
- exec
- search
- split
- match
- replace
参考链接
参考书
- 《JavaScript正则迷你书》
正则可视化编辑调试器
- [Regulex(纯JS编写)](jex.im/regulex/#!f…
- Pyregex,支持python
- Debuggex,支持JavaScript、Python