正则表达式概述
正则表达式(regular expression)是一种表达文本模式(即字符串结构)的方法,有点像字符串的模板,常常用来按照“给定模式”匹配文本。比如,正则表达式给出一个 Email 地址的模式,然后用它来确定一个字符串是否为 Email 地址。
正则表达式的作用
- 给定的字符串是否符合正则表达式的过滤逻辑(匹配)
- 可以通过正则表达式,从字符串中获取我们想要的特定部分(提取)
- 强大的字符串替换能力(替换)
正则表达式的特点
- 灵活性、逻辑性和功能性非常的强
- 可以迅速地用极简单的方式达到字符串的复杂控制
创建一个正则表达式
1. 使用字面量以斜杠表示开始和结束
var regex = /xyz/;
2. 另一种是使用RegExp构造函数
var regex = new RegExp('xyz');
上面两种写法是等价的,都新建了一个内容为xyz的正则表达式对象。它们的主要区别是,第一种方法在引擎编译代码时,就会新建正则表达式,第二种方法在运行时新建正则表达式,所以前者的效率较高。而且,前者比较便利和直观,所以实际应用中,基本上都采用字面量定义正则表达式。
RegExp构造函数还可以接受第二个参数,表示修饰符,如,
var regex = new RegExp('xyz', 'i');
// 等价于
var regex = /xyz/i;
修饰符
修饰符(modifier)表示模式的附加规则,放在正则模式的最尾部。
修饰符可以单个使用,也可以多个一起使用。
// 单个修饰符
var regex = /test/i;
// 多个修饰符
var regex = /test/ig;
g 修饰符
默认情况下,第一次匹配成功后,正则对象就停止向下匹配了。g修饰符表示全局匹配(global),加上它以后,正则对象将匹配全部符合条件的结果,主要用于搜索和替换。正则模式不含g修饰符,每次都是从字符串头部开始匹配.正则模式含有g修饰符,每次都是从上一次匹配成功处,开始向后匹配
var regex = /b/g;
var str = 'abba';
regex.test(str); // true
regex.test(str); // true
regex.test(str); // false
i修饰符
正则对象区分字母的大小写,加上i修饰符以后表示忽略大小写(ignoreCase)加了i修饰符以后,不考虑大小写。
m 修饰符
m修饰符表示多行模式(multiline),会修改和$的行为。默认情况下(即不加m修饰符时),^和$匹配字符串的开始处和结尾处,加上m修饰符以后,和会识别换行符(\n)。
/world$/.test('hello world\n') // false
/world$/m.test('hello world\n') // true
上面的代码中,字符串结尾处有一个换行符。如果不加m修饰符,匹配不成功,因为字符串的结尾不是world;加上以后,$可以匹配行尾。
正则表达式实例方法
RegExp.prototype.test()
正则实例对象的test方法返回一个布尔值,表示当前模式是否能匹配参数字符串。
/cat/.test('cats and dogs') // true
如果正则表达式带有g修饰符,则每一次test方法都从上一次结束的位置开始向后匹配
var r = /x/g;
var s = '_x_x';
r.lastIndex // 0
r.test(s) // true
r.lastIndex // 2
r.test(s) // true
r.lastIndex // 4
r.test(s) // false
上面代码的正则表达式使用了g修饰符,表示是全局搜索,会有多个结果。接着,三次使用test方法,每一次开始搜索的位置都是上一次匹配的后一个位置。
注意,带有g修饰符时,正则表达式内部会记住上一次的lastIndex属性,这时不应该更换所要匹配的字符串,否则会有一些难以察觉的错误。
var r = /bb/g;
r.test('bb') // true
r.test('-bb-') // false
RegExp.prototype.exec()
正则实例对象的exec()方法,用来返回匹配结果。如果发现匹配,就返回一个数组,成员是匹配成功的子字符串,否则返回null。
var s = '_x_x';
var r1 = /x/;
var r2 = /y/;
r1.exec(s) // ["x"]
r2.exec(s) // null
如果正则表达式包含圆括号(即含有“组匹配”),则返回的数组会包括多个成员。第一个成员是整个匹配成功的结果,后面的成员就是圆括号对应的匹配成功的组。也就是说,第二个成员对应第一个括号,第三个成员对应第二个括号,以此类推。整个数组的length属性等于组匹配的数量再加1。
exec()方法的返回数组还包含以下两个属性:
- input:整个原字符串。
- index:模式匹配成功的开始位置(从0开始计数)。
var r = /a(b+)a/;
var arr = r.exec('_abbba_aba_');
arr // ["abbba", "bbb"]
arr.index // 1
arr.input // "_abbba_aba_"
利用g修饰符允许多次匹配的特点,可以用一个循环完成全部匹配。
var reg = /a/g;
var str = 'abc_abc_abc'
while(true) {
var match = reg.exec(str);
if (!match) break;
console.log('#' + match.index + ':' + match[0]);
}
// #0:a
// #4:a
// #8:a
上面代码中,只要exec()方法不返回null,就会一直循环下去,每次输出匹配的位置和匹配的文本。
正则实例对象的lastIndex属性不仅可读,还可写。设置了g修饰符的时候,只要手动设置了lastIndex的值,就会从指定位置开始匹配。
字符串方法
String.prototype.match()
字符串实例对象的match方法对字符串进行正则匹配,返回匹配结果。默认情况下,match()方法只会找到第一个符合要求的内容,找到以后就停止检索。我们可以设置正则表达式为全局匹配模式,这样就会匹配到所有的内容,并以数组的形式返回。
var str = "1a2a3a4a5e6f7A8B9C";
var result1 = str.match(/[a-z]/); // 找到符合要求的第一个内容,然后返回
var result2 = str.match(/[a-z]/g); // 设置为“全局匹配”模式,匹配字符串中 所有的小写字母
var result3 = str.match(/[a-z]/gi); // 设置多个匹配模式,匹配字符串中 所有的字母(忽略大小写)
console.log(result1); // 打印结果:["a"]
console.log(result2); // 打印结果:["a", "a", "a", "a", "e", "f"]
console.log(result3); // 打印结果:["a", "a", "a", "a", "e", "f", "A", "B", "C"]
String.prototype.search()
字符串对象的search方法,返回第一个满足条件的匹配结果在整个字符串中的位置。如果没有任何匹配,则返回-1。search()方法可以接受一个正则表达式作为参数,然后会根据正则表达式去检索字符串。serach()只会查找第一个,即使设置全局匹配也没用。
var str = "hello abc hello aec afc";
/*
* 搜索字符串中是否含有abc 或 aec 或 afc
*/
result = str.search(/a[bef]c/);
console.log(result); // 打印结果:6
String.prototype.replace()
字符串对象的replace方法可以替换匹配的值。它接受两个参数,第一个是正则表达式,表示搜索模式,第二个是替换的内容。
str.replace(被替换的内容, 新的内容)
参数解释:
- 被替换的内容:可以接受一个正则表达式作为参数。
- 新的内容:默认只会替换第一个。如果需要替换全部符合条件的内容,可以设置正则表达式为全局匹配模式。
//replace()方法:替换
var str2 = "Today is fine day,today is fine day !!!"
console.log(str2);
console.log(str2.replace("today","tomorrow")); //只能替换第一个today
console.log(str2.replace(/today/gi,"tomorrow")); //这里用到了正则,且为“全局匹配”模式,才能替换所有的today
String.prototype.split()
字符串对象的split方法按照正则规则分割字符串,返回一个由分割后的各个部分组成的数组。该方法接受两个参数,第一个参数是正则表达式,表示分隔规则,第二个参数是返回数组的最大成员数。
str.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' ]
正则表达式匹配规则
常用元字符串
元字符 | 说明 |
---|---|
\d | 匹配数字 |
\D | 匹配任意非数字的字符 |
\w | 匹配字母或数字或下划线 |
\W | 匹配任意不是字母,数字,下划线 |
\s | 匹配任意的空白符 |
\S | 匹配任意不是空白符的字符 |
. | 匹配除换行符以外的任意单个字符 |
^ | 表示匹配行首的文本(以谁开始) |
$ | 表示匹配行尾的文本(以谁结束) |
限定符
限定符 | 说明 |
---|---|
* | 重复零次或更多次 |
+ | 重复一次或更多次 |
? | 重复零次或一次 |
{n} | 重复n次 |
{n,} | 重复n次或更多次 |
{n,m} | 重复n到m次 |
其他
- [] 字符串用中括号括起来,表示匹配其中的任一字符,相当于或的意思
- [^] 匹配除中括号以内的内容
- \ 转义符
- | 或者,选择两者中的一个。注意|将左右两边分为两部分,而不管左右两边有多长多乱
- () 从两个直接量中选择一个,分组
正则表达式贪婪匹配
默认情况下都是最大可能匹配,即匹配到下一个字符不满足匹配规则为止。这被称为贪婪模式。
var s = 'aaa';
s.match(/a+/) // ["aaa"]
上面代码中,模式是/a+/,表示匹配1个a或多个a,那么到底会匹配几个a呢?因为默认是贪婪模式,会一直匹配到字符a不出现为止,所以匹配结果是3个a。
除了贪婪模式,还有非贪婪模式,即最小可能匹配。只要一发现匹配,就返回结果,不要往下检查。如果想将贪婪模式改为非贪婪模式,可以在量词符后面加一个问号。
var s = 'aaa';
s.match(/a+?/) // ["a"]
上面例子中,模式结尾添加了一个问号/a+?/,这时就改为非贪婪模式,一旦条件满足,就不再往下匹配,+?表示只要发现一个a,就不再往下匹配了。
除了非贪婪模式的加号(+?),还有非贪婪模式的星号(*?)和非贪婪模式的问号(??)。
- +?:表示某个模式出现1次或多2 次,匹配时采用非贪婪模式。
- *?:表示某个模式出现0次或多次,匹配时采用非贪婪模式。
- ??:表格某个模式出现0次或1次,匹配时采用非贪婪模式。
组匹配
正则表达式的括号表示分组匹配,括号中的模式可以用来匹配分组的内容。
/fred+/.test('fredd') // true
/(fred)+/.test('fredfred') // true
分组捕获
var m = 'abcabc'.match(/(.)b(.)/);
m
// ['abc', 'a', 'c']
上面代码中,正则表达式/(.)b(.)/一共使用两个括号,第一个括号捕获a,第二个括号捕获c。注意,使用组匹配时,不宜同时使用g修饰符,否则match方法不会捕获分组的内容。
括号还可以嵌套
/y((..)\2)\1/.test('yabababab') // true
上面代码中,\1指向外层括号,\2指向内层括号。
非捕获组
(?:x)称为非捕获组(Non-capturing group),表示不返回该组匹配的内容,即匹配的结果中不计入这个括号。
非捕获组的作用请考虑这样一个场景,假定需要匹配foo或者foofoo,正则表达式就应该写成/(foo){1, 2}/,但是这样会占用一个组匹配。这时,就可以使用非捕获组,将正则表达式改为/(?:foo){1, 2}/,它的作用与前一个正则是一样的,但是不会单独输出括号内部的内容。
var m = 'abc'.match(/(?:.)b(.)/);
m // ["abc", "c"]
先行断言和先行否定断言
x(?=y)称为先行断言(Positive look-ahead),x只有在y前面才匹配,y不会被计入返回结果。比如,要匹配后面跟着百分号的数字,可以写成/\d+(?=%)/。
x(?!y)称为先行否定断言(Negative look-ahead),x只有不在y前面才匹配,y不会被计入返回结果。比如,要匹配后面跟的不是百分号的数字,就要写成/\d+(?!%)/。