正则,就是用来处理字符串的一种规则。
- 正则只能用于处理字符串
- 处理一般包含两个方面
- 验证当前字符串是否符合某个规则,正则匹配
- 把一个字符串中国符合规则的字符获取到,正则捕获
正则的使用跟字符串是分不开的,使用正则有两种方法:
- 使用正则实例的方法,需要传入字符串
- 使用字符串实例的方法,需要传入正则
1. 正则的方法
test 匹配
查找这个字符串是否满足正则规律,匹配成功返回 true
,否则返回 false
(1)正则中没有全局匹配时,每次都是从开头匹配,只要有符合的就返回 true
var reg = /\d{2}/;
var str = 'a11a';
reg.test(str); //=> true
(2)正则中有全局匹配的时候,每次都会从 reg.lastIndex
索引开始匹配,只要匹配到后面都没有,则返回 false
,此时索引会重置。与 exec
方法一样。
var reg = /\d{2}/g;
var str = 'a11a11';
reg.test(str); //=> true
reg.test(str); //=> true
reg.test(str); //=> false
exec 捕获
在字符串中获取满足正则规律的字符串,匹配成功则返回一个数组,没有匹配到则返回 null
返回一个数组,数组中第一项是匹配的字符串,后面的项是匹配到的分组。
其中还有两个属性:
input
:源字符串index
:匹配到的开始索引(从0开始)
(1)正则中没有全局匹配的时候- 无论执行多少次,都是从头开始匹配
- 都只能获取到第一个匹配,以及匹配的分组。
var reg = /\d+/;
var str = 'aa2018aa2019';
reg.exec(str); //=> 2018
reg.exec(str); //=> 2018
(2)正则中有全局匹配的时候
- 每次执行的时候,会从上一次记录的
reg.lastIndex
索引开始查找,也就是每次查找的开始位置不一样 - 即使再次使用该方法传入的字符串不一样,也会从上一次记录的
reg.lastIndex
索引开始查找 - 每次使用这个方法,依然只获取第一次匹配到的字符串以及其中的分组
var reg = /\d+/g;
var str = 'aa2018aa2019';
var str2 = 'aa2018aa2019';
reg.exec(str); //=> 2018
reg.exec(str2); //=> 2019,说明也是从上一次索引开始查找的
reg.exec(str); //=> null
reg.exec(str2); //=> 2018
需要注意的是:
> 1. 当它遍历到最后,再使用此方法,会返回有一个null
,此时reg.lastIndex
也会重置回0
,之后又会从开头重新遍历
> 2. 使用同一个正则,那么其记录的reg.lastIndex
是一样的,与字符串无关,这样就会出现不可预料的结果
> 3. 使用同一个正则的 test 或 exec 方法,也是相同的reg.lastIndex
,就会相互影响,结果不可预料
由于exec
没有办法一次捕获所有匹配的字符串,同时也存在检测不同字符时,不是从开头匹配的 bug
所以,需要自己写一个execAll
方法,捕获了所有的匹配字符串,并且最终索引都会回到最初。
RegExp.prototype.execAll = function (str) {
var temp;
//=> 防止后面出现死循环,若没有全局修饰符,直接给它返回匹配到的第一个
if(!this.global) {
temp = this.exec(str);
temp.errorReason = "你没有添加全局修饰符";
return temp;
}
var ary = [];
temp = this.exec(str);
while (temp) { //=> 当 exec 没有捕获到时,返回 null,同时索引置回最初
ary.push(temp[0]);
temp = this.exec(str);
}
return ary;
}
2. 字符串的方法
match 匹配
match
方法用于确定原字符串是否匹配某个子字符串,返回一个数组,成员为匹配的第一个字符串。如果没有找到匹配,则返回 null
。
'cat, bat, sat, fat'.match('at') // ["at"]
'cat, bat, sat, fat'.match('xt') // null
返回数组还有 index
属性和 input
属性,分别表示匹配字符串开始的位置和原始字符串。
var matches = 'cat, bat, sat, fat'.match('at');
matches.index // 1
matches.input // "cat, bat, sat, fat"
match
方法还可以使用正则表达式作为参数,行为在很大程度上有赖于 regexp 是否具有标志 g
。
(1)如果 regexp 没有标志 g
,就只能在源字符串中执行一次匹配。与 exec
一样
- 如果没有找到任何匹配的文本, 返回
null
。 - 否则,它将返回一个数组,其中存放了与它找到的匹配文本有关的信息。
- 该数组的第 0 个元素存放的是匹配文本,而其余的元素存放的是正则的分组捕获到的内容
- 返回的数组还含有两个对象属性。
index
属性声明的是匹配文本的起始字符在源字符串中的位置,input
属性声明的是对源字符串的引用。
(2)如果 regexp 具有标志g
,将执行全局检索,找到源字符串中的所有匹配子字符串。
- 若没有找到任何匹配的子串,返回
null
- 如果找到了一个或多个匹配子串,则返回一个数组。
- 全局匹配返回的数组的内容与前者大不相同,它的数组元素中存放的是源字符串中所有的匹配子串,不再捕获分组
- 没有
index
属性或input
属性。
> 注意:在全局检索模式下,match()
不提供与子表达式匹配的文本的信息,也不声明每个匹配子串的位置。如果您需要这些全局检索的信息,可以使用exec()
。
split 分割
split
方法把字符串按照指定字符串进行分割成数组。传入的参数也可以是正则表达式。
传入参数是正则表达式的时候,会按照正则匹配到的字符串进行分割。
不管有没有全局匹配,都是按照全局搜索去匹配的。
var reg = /[,+-]/;
var str = 'aa,bb+cc-aa';
str.split(reg); //=> ["aa", "bb", "cc", "aa"];
var reg = /[,+-]/g;
var str = 'aa,bb+cc-aa';
str.split(reg); //=> ["aa", "bb", "cc", "aa"];
replace 替换
replace 用于将某个字符串替换成另外一个字符串,只会替换第一个。传入的第一个参数可以是正则表达式,此时会替换掉匹配到的字符串
第一个参数是正则时:
- 如果 regexp 具有全局标志
g
,那么将替换所有匹配的子串。 - 否则,它只替换第一个匹配子串。
当正则中没有全局匹配时,只会匹配到第一个,并且让替换它
var reg = /\d+/;
var str = "aa2018aa2019";
str.replace(reg, ','); //=> "aa,aa2019"
var reg = /\d+/g;
var str = "aa2018aa2019";
str.replace(reg, ','); //=> "aa,aa,"
第二个参数可以是字符串,也可以是函数:
- 如果它是字符串,那么每个匹配都将由字符串替换。但是替换字符串中的
$
字符具有特定的含义。如下表所示,它说明从模式匹配得到的字符串将用于替换。
|字符|替换文本|
|:—-:—-
|$1、$2、…、$99| 与 regexp 中的第 1 到第 99 个子表达式相匹配的文本。|
|$& |与 regexp 相匹配的子串。|
|$` |位于匹配子串左侧的文本。|
|$’|位于匹配子串右侧的文本。|
|$$ |直接量符号。| - 如果是函数,每次匹配到内容都调用一次该函数,它返回的字符串将作为替换文本使用。
- 该函数的第一个参数是匹配模式的字符串
- 接下来的参数是与模式中的子表达式匹配的字符串,可以有 0 个或多个这样的参数
- 接下来的参数是一个整数,声明了匹配在源字符串中出现的位置
- 最后一个参数是源字符串本身
每一次 ARG 中存储的信息。和执行 exec 捕获的信息相似(内置原理:每一次正则匹配到的结果,都把函数执行,然后基于 exec 把本次匹配的信息捕获到,然后把捕获的信息传递给这个函数)
var reg = /(\d+)/g;
var str = "aa2018aa2019";
var str2 = str.replace(reg2, function() {
consoel.log(arguments);
//=> 第一项:匹配到的内容 str
//=> 第二项:分组匹配到的内容,不只一项 str1,str2,str3...
//=> 第三项:匹配内容的位置 index
//=> 第四项:源字符串本身
//=> return 返回的字符串用以替换匹配到的内容
})
把字符串中的数字加 1
var reg = /\d+/g;
var str = "aa2018aa2019";
var str3 = str.replace(reg, function() {
var temp = arguments[0];
temp = temp * 1 + 1; //=> 这里的 *1 将前面的字符串转换为数字
return temp;
})
把字符串中的数字每一位都加 1
var reg = /\d/g; //=> 这里没有 +
var str = "aa2018aa2019";
var str3 = str.replace(reg, function() {
var temp = arguments[0];
temp = temp * 1 + 1;
return temp;
})
3. 正则的懒惰性
正则的捕获有懒惰性:只能捕获到第一个匹配的内容,剩余的默认捕获不到。
正则的懒惰性与正则的 lastIndex
属性有关,它代表正则捕获的时候,下一次在字符串中开始查找的索引。
//=> lastIndex 不变,导致了正则捕获的懒惰性
let str = 'aa18bb19';
let reg = /\d+/;
reg.lastIndex; //=> 0
reg.exec(str); //=> ['18']
reg.lastIndex; //=> 0
reg.exec(str); //=> ['18']
//=> 手动改变 lastIndex 值,不起作用
reg.lastIndex = 4;
reg.lastIndex; //=> 4
reg.exec(str); //=> ['18']
//=> 需要使用全局匹配
let str = 'aa18bb19';
let reg2 = /\d+/g;
reg2.lastIndex; //=> 0
reg2.exec(str); //=> ['18']
reg2.lastIndex; //=> 4
reg2.exec(str); //=> ['19']
reg2.lastIndex; //=> 8
reg2.exec(str); //=> null
reg2.lastIndex; //=> 0
reg2.exec(str); //=> ['18']
解决正则捕获的懒惰性,需要添加全局修饰符 g
,这个是唯一的方法,而且不加 g
,不管用什么办法捕获,也都不能把全部匹配捕获到。
4. 正则的贪婪性
出现量词的时候,每次匹配捕获的时候,总是捕获到和正则匹配中最长的内容。
let str = 'aa2018';
let reg1 = /\d+/g;
reg1.exec(str); //=> '2018'
//=> 把问号放在量词元字符后面,代表的就不是出现零次或者一次了,而是取消捕获的贪婪性
let reg2 = /\d+?/g;
reg2.exec(str); //=> '2'