# 概述
正则表达式(regular expression)是一个描述字符模式的对象,有点像字符串的模板,可以对文本进行强大的模式匹配和文本检索与替换功能。 新建正则表达式有两种方式。一种是使用字面量,以斜杠表示开始和结束。
var regex = /java/;
另一种是使用RegExp构造函数
var regex = new RegExp('java');
这两种方法都是等价的。下面介绍正则表达式的匹配规则
# 匹配规则
# 元字符
大部分字符在正则表达式中,就是字面的含义,比如/java/匹配java, 除了字面量字符以外,还有一部分字符有特殊含义,不代表字面的意思。正则表达式严格区分大小写,这些字符叫做“元字符”,主要有以下几个:
元字符 | 描述 |
---|---|
. | 匹配除换行符以外的任意字符 |
\d | 匹配数字,相当于字符组[0-9] |
\w | 匹配字母, 数字, 下划线 |
\s | 匹配任意的空白符(包括制表符,空格,换行等) |
\b | 匹配单词开始或结束的位置 |
^ | 匹配行首 |
$ | 匹配行尾 |
# 点字符
点字符(.)匹配除回车(\r)、换行(\n)、行分隔符(\u2028)和段分隔符(\u2029)以外的字符
/a.c/
上面代码中a.c匹配a和c之间包含任意一个字符的情况,只要是这三个字符在同一行即可,可以匹配abc但不匹配abbc四个字符的情况。
# 选择符
选择符(|)表示'or'的选择关系,选择符会包括它前后的多个字符,比如/ab|cd/匹配ab或者cd,而不是b或者c
# 反义元字符
元字符 | 描述 |
---|---|
\D | 匹配非数字的任意字符,等价于[^0-9] |
\W | 匹配除字母,数字,下划线以外的任意字符 |
\S | 匹配非空白的任意字符 |
\B | 匹配非单词开始或结束的位置 |
[^x] | 匹配除x以外的任意字符 |
# 重复限定符
假设重复次数为n
|限定符|描述|记忆方式| |:----------😐:-----------😐 |*|n>=0|看看天上的星星,可能一颗没有,可能零散有几颗,可能数也数不过来。| |+|n>=1|加号是追加的意思,得先有一个,然后才考虑追加。| |?|n=0 or n=1|问号的意思表示,有吗?| |{x}|n=x|| |{x,}|n>=x|| |{x,y}|x<=n<=y||
# 贪婪模式和非贪婪模式
默认情况下,上一节的限定词都是贪婪模式,即尽可能最大可能的匹配,即匹配直到下一个字符不满足匹配规则为止。
var str = 'aaa'
str.match(/a+/) // aaa 贪婪模式
str.match(/a+?/) // a 非贪婪模式
2
3
上面代码,默认是贪婪模式,会一直匹配到字符a没有为止,所以会匹配到3个a. 如果在限定词后增加?,则是非贪婪模式,表示尽可能少的去捕获字符
# 字符类
可供选择的字符放在方括号[]内,表示匹配其中一个字符。比如:[xyz]匹配字符x, y或z,如果中括号中包含元字符,则元字符不在具有元字符的功能,比如[+.?]匹配加号,点号或问号。 有两个字符在字符类中有特殊含义
# 脱字符(^)
[^xyz]表示除了xyz中的字符都可以匹配,如果方括号内没有其他字符,就表示匹配一切字符,其中包括换行符,而点号(.)是不包括换行符的,其中要注意的是脱字符只有在字符类的第一个位置才有特殊意义,否则就是字面含义
# 连字符(-)
对于连续序列的字符,连字符(-)可以提供简写形式,表示字符的连续范围。比如[a-z]表示26个小写字母,但是当连字符不出现在方括号内,就不具备简写的作用,只代表字面的含义。 不要过分使用连字符,设定一个很大的范围,否则很可能选中意料之外的字符。最典型的例子就是[A-z],表面上它是选中从大写的A到小写的z之间52个字母,但是由于在ASCII编码之中,大写字母与小写字母之间还有其他字符,结果就会出现意料之外的结果。
/[A-z]/.test('\\') // true
上面代码中,由于反斜杠(\)的ASCII码在大写字母与小写字母之间,结果会被选中。
# 分组
正则表达式中的小括号表示分组匹配
/(abc)+/.test('abcabc') // true
分组可以方便的表示重复次数,还可以用于捕获,请看捕获性分组
# 捕获性分组
捕获性分组,通常由一对小括号加上子表达式组成,捕获分组所匹配的内容暂储存在内存中以便下次使用,捕获性分组会创建反向引用,每个反向引用都用一个编号或名称来标识,主要通过$+编号或者+编号表示法进行引用
var reg = 'abcabc'.match(/(.)b(.)/)
console.log(reg) // ['abc', 'a', 'c']
console.log((RegExp.$1)) // a
console.log((RegExp.$2)) // c
2
3
4
上面代码中,正则表达式/(.)b(.)/使用了两个括号,第一个括号捕获a,第二个括号捕获c。
RegExp.
var reg = 'abcabc'.match(/(.)b(.)/g) // ['abc', 'abc']
上面代码使用了带g修饰符的正则表达式,结果match方法只捕获了匹配整个表达式的部分
# 反向引用
获取捕获性分组所匹配结果的过程称为“反向引用”。在正则表达式内部,可以用\n引用括号匹配的内容,n是从1开始的自然数,表示对应顺序的括号。
/(.)b(.)\1b\2/.test('abcabc') // true
上面代码中,\1表示第一个括号匹配的内容,\2表示第二个括号匹配的内容。括号也可以嵌套
/a((..)\2)\1/.test('adededede') // true
上面代码中,\1指向外层括号,\2指向内层括号,按照从左到右左括号出现的顺序确定编号。
# 非捕获性分组
(?:x)称为非捕获性分组,表示不返回该组匹配的内容,即匹配的结果中不计入这个括号。 它的作用是在要使用括号的情况下,不会占用一个组匹配。比如需要匹配foo或者foofoo,正则表达式可写/(foo){1, 2}/,但是这样就占用了一个组匹配,写成/(?:foo){1, 2}/作用与前一个正则表达式一样,但是不会占用组匹配
var result = 'abc'.match(/(?:.)b(.)/) // ['abc', 'c']
上面代码中,第一个括号是非捕获性分组,所以最后返回的结果没有第一个括号的内容,只有第二个括号匹配的内容。
# 先行断言
先行断言的形式为x(?=y),x只有在y前面才匹配,y不会计入返回结果,括号内的部分不会返回到结果
var result = 'abc'.match(/b(?=c)/) // ['b']
上面代码使用了先行断言,b在c前面所以被匹配,但是括号对应的c不会被返回
# 先行否定断言
先行否定断言的形式为x(?!y),x只有不在y前面才匹配,y不会计入返回结果
var result = 'abdbc'.match(/b(?!c)/) // ['b]
上面代码使用了先行否定断言,b不在c前面所以被匹配,但是括号对应的c不会被返回
# 参考资料
[1] 阮一峰, JavaScript标准参考教程 [2] 路易斯, 正则表达式前端使用手册