共计 4266 个字符,预计需要花费 11 分钟才能阅读完成。
前言
对于前端开发来说,正则表达式是我们避不开的且需要重点掌握的技能,作为一门用途很广,但是又常常让人望而生畏的技术。花时间去深入理解并且融合贯通是值得。
基本概念
什么是正则表达式?正则表达式是一种表达文本模式(即字符串结构)的方法,有点像字符串的模板,常常用来匹配文本。通过 特殊字符 + 普通字符
来进行模式描述,从而达到文本匹配目的工具。
声明方式
新建正则表达式有两种方法并且以斜杠表示开始和结束。
1、字面量声明
var regex = /xyz/;
2、构造函数声明
var regex = new RegExp('xyz');
3、ES6 构造函数声明
ES6
中 RegExp
构造函数第一个参数是一个正则对象,那么可以使用第二个参数指定修饰符。ES5
不支持这种方式。
var regex = new RegExp(/xyz/ig, 'i');
regex.flags // 'i'
上面代码中,原有正则对象的修饰符是 ig
,它会被第二个参数i
覆盖。
正则表达式语法
实例方法
1、test()
正则实例对象的 test 方法返回一个布尔值,表示当前模式是否能匹配参数字符串。
/cat/.test('cats and dogs') // true
2、exec()
正则实例对象的 exec
方法,用来返回匹配结果。如果发现匹配,就返回一个数组,成员是匹配成功的子字符串,否则返回null
。
var s = '_x_x';
var r1 = /x/;
var r2 = /y/;
r1.exec(s) // ["x"]
r2.exec(s) // null
修饰符
修饰符表示模式的附加规则,放在正则模式的最尾部。修饰符可以单个使用,也可以多个一起使用。
1、ES5 中修饰符
g
:global
执行一个全局匹配,匹配所有结果。加上它以后,正则对象将匹配全部符合条件的结果,主要用于搜索和替换。
默认情况下,第一次匹配成功后,正则对象就停止向下匹配了
var regex = /b/g;
var str = 'abba';
regex.test(str); // true
regex.test(str); // true
regex.test(str); // false
i
:ignorecase
执行一个不区分大小写的匹配。
/abc/.test('ABC') // false
/abc/i.test('ABC') // true
m
:multiple
lines
多行匹配。
/world$/.test('hello world\n') // false
/world$/m.test('hello world\n') // true
2、ES6 中新增修饰符
u
修饰符
ES6 对正则表达式添加了 u 修饰符,含义为“Unicode 模式”,用来正确处理大于 uFFFF 的 Unicode 字符。也就是说,会正确处理四个字节的 UTF-16 编码。
/^\uD83D/u.test('\uD83D\uDC2A') // false
/^\uD83D/.test('\uD83D\uDC2A') // true
y
修饰符
ES6
还为正则表达式添加了 y 修饰符,叫做“粘连”(sticky
)修饰符。
y
修饰符的作用与 g
修饰符类似,也是全局匹配,后一次匹配都从上一次匹配成功的下一个位置开始。不同之处在于,g
修饰符只要剩余位置中存在匹配就可,而 y 修饰符确保匹配必须从剩余的第一个位置开始,这也就是“粘连”的涵义。
var s = 'aaa_aa_a';
var r1 = /a+/g;
var r2 = /a+/y;
r1.exec(s) // ["aaa"]
r2.exec(s) // ["aaa"]
r1.exec(s) // ["aa"]
r2.exec(s) // null
基本要素
字符类
1、点字符(.)
匹配除回车、换行、行分隔符和段分隔符以外任意一个字符
/c.t/
2、[]
匹配括号中的任意一个字符
/[abc]/.test('hello world') // false
/[abc]/.test('apple') // true
3、连字符(-)
在 []
内表示字符范围
/a-z/.test('b') // false
/[a-z]/.test('b') // true
4、脱字符(^)
如果方括号内的第一个字符是[^]
,则表示除了字符类之中的字符,其他字符都可以匹配。
/[^abc]/.test('bbc news') // true
/[^abc]/.test('bbc') // false
数量限定符
1、?
问号表示某个模式出现 0
次或 1
次,等同于{0, 1}
。
// t 出现 0 次或 1 次
/t?est/.test('test') // true
/t?est/.test('est') // true
2、+
加号表示某个模式出现 1
次或多次,等同于{1,}
。
// t 出现 1 次或多次
/t+est/.test('test') // true
/t+est/.test('ttest') // true
/t+est/.test('est') // false
3、*
星号表示某个模式出现 0
次或多次,等同于{0,}
。
// t 出现 0 次或多次
/t*est/.test('test') // true
/t*est/.test('ttest') // true
/t*est/.test('tttest') // true
/t*est/.test('est') // true
4、{n}
表示恰好重复 n
次。
/lo{2}k/.test('look') // true
5、{n,}
表示至少重复 n
次。
/lo{2,}k/.test('looook') // true
6、{n,m}
表示重复不少于 n
次,不多于 m
次。
/lo{2,5}k/.test('looook') // true
位置限定符
1、^
表示字符串的开始位置
// test 必须出现在开始位置
/^test/.test('test123') // true
2、$
表示字符串的结束位置
// test 必须出现在结束位置
/test$/.test('new test') // true
特殊字符
1、转义符(\)
/1+1/.test('1+1')
// false
/1\+1/.test('1+1')
// true
2、()
将正则表达式的一部分括起来组成一个单元,可以对整个单元使用数量限定符
([0-9]{1,3}\.){3}[0-9]{1-3} // 匹配 ip 地址
3、选择符(|)
竖线符号 (|)
在正则表达式中表示“或关系”。
/11|22/.test('911') // true
预定义模式
预定义模式指的是某些常见模式的简写方式。
-
\d
匹配0-9
之间的任一数字,相当于[0-9]
。 -
\D
匹配所有0-9
以外的字符,相当于[^0-9]
。 -
\w
匹配任意的字母、数字和下划线,相当于[A-Za-z0-9_]
。 -
\W
除所有字母、数字和下划线以外的字符,相当于[^A-Za-z0-9_]
。 -
\s
匹配空格(包括换行符、制表符、空格符等),相等于[\t\r\n\v\f]
。 -
\S
匹配非空格的字符,相当于[^ \t\r\n\v\f]
。 -
\b
匹配词的边界。 -
\B
匹配非词边界,即在词的内部。
进阶使用
字符串正则方法
字符串对象共有 4 个方法,可以使用正则表达式:match()
、replace()
、search()
和split()
。
1、match()
字符串实例对象的 match 方法对字符串进行正则匹配,返回匹配结果。
var s = '_x_x';
var r1 = /x/;
var r2 = /y/;
s.match(r1) // ["x"]
s.match(r2) // null
2、replace()
字符串对象的 replace 方法可以替换匹配的值。它接受两个参数,第一个是正则表达式,表示搜索模式,第二个是替换的内容。
正则表达式如果不加 g 修饰符,就替换第一个匹配成功的值,
否则替换所有匹配成功的值。
'aaa'.replace('a', 'b') // "baa"
'aaa'.replace(/a/, 'b') // "baa"
'aaa'.replace(/a/g, 'b') // "bbb"
3、search()
字符串对象的 search
方法,返回第一个满足条件的匹配结果在整个字符串中的位置。如果没有任何匹配,则返回-1
。
'_x_x'.search(/x/)
// 1
4、split()
字符串对象的 split 方法按照正则规则分割字符串,返回一个由分割后的各个部分组成的数组。
该方法接受两个参数,第一个参数是正则表达式,表示分隔规则,第二个参数是返回数组的最大成员数。
// 非正则分隔
'a, b,c, d'.split(',')
// ['a', 'b', 'c', 'd']
// 正则分隔,去除多余的空格
'a, b,c, d'.split(/, */)
// ['a', 'b', 'c', 'd']
// 指定返回数组的最大成员
'a, b,c, d'.split(/, */, 2)
['a', 'b']
贪婪模式
?、*、+
匹配符默认情况下都是最大可能匹配,即匹配直到下一个字符不满足匹配规则为止。这被称为贪婪模式。
var s = 'aaa';
s.match(/a+/) // ["aaa"]
非贪婪模式
-
+?
:表示某个模式出现 1 次或多次,匹配时采用非贪婪模式。 -
*?
:表示某个模式出现 0 次或多次,匹配时采用非贪婪模式。 -
??
:表格某个模式出现 0 次或 1 次,匹配时采用非贪婪模式。
'abb'.match(/ab*b/) // ["abb"]
'abb'.match(/ab*?b/) // ["ab"]
'abb'.match(/ab?b/) // ["abb"]
'abb'.match(/ab??b/) // ["ab"]
组匹配
正则表达式的括号表示分组匹配,括号中的模式可以用来匹配分组的内容。
/fred+/.test('fredd') // true
/(fred)+/.test('fredfred') // true
1、分组捕获
var m = 'abcabc'.match(/(.)b(.)/);
m
// ['abc', 'a', 'c']
// 全局
var m = 'abcabc'.match(/(.)b(.)/g);
m // ['abc', 'abc']
2、非捕获组
(?:x)
称为非捕获组,表示不返回该组匹配的内容,即匹配的结果中不计入这个括号。
var m = 'abc'.match(/(?:.)b(.)/);
m // ["abc", "c"]
3、先行断言
x(?=y)
称为先行断言,x 只有在 y 前面才匹配,y 不会被计入返回结果。
var m = 'abc'.match(/b(?=c)/);
m // ["b"]
4、先行否定断言
x(?!y)
称为先行否定断言,x 只有不在 y 前面才匹配,y 不会被计入返回结果。
/\d+(?!\.)/.exec('3.14')
// ["14"]
总结
正则表达式是开发中经常需要使用的技术,也是前端必须掌握的技能,它的重要性不言而喻。不熟练的童鞋加紧学习。