正则,就是用来处理字符串的一种规则。
- 正则只能用于处理字符串
- 处理一般包含两个方面
- 验证当前字符串是否符合某个规则,正则匹配
- 把一个字符串中国符合规则的字符获取到,正则捕获
正则的使用跟字符串是分不开的,使用正则有两种方法:
- 使用正则实例的方法,需要传入字符串
- 使用字符串实例的方法,需要传入正则
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); //=> truereg.test(str); //=> truereg.test(str); //=> false
exec 捕获
在字符串中获取满足正则规律的字符串,匹配成功则返回一个数组,没有匹配到则返回 null
返回一个数组,数组中第一项是匹配的字符串,后面的项是匹配到的分组。
其中还有两个属性:
input:源字符串index:匹配到的开始索引(从0开始)
(1)正则中没有全局匹配的时候- 无论执行多少次,都是从头开始匹配
- 都只能获取到第一个匹配,以及匹配的分组。
var reg = /\d+/;var str = 'aa2018aa2019';reg.exec(str); //=> 2018reg.exec(str); //=> 2018
(2)正则中有全局匹配的时候
- 每次执行的时候,会从上一次记录的
reg.lastIndex索引开始查找,也就是每次查找的开始位置不一样 - 即使再次使用该方法传入的字符串不一样,也会从上一次记录的
reg.lastIndex索引开始查找 - 每次使用这个方法,依然只获取第一次匹配到的字符串以及其中的分组
var reg = /\d+/g;var str = 'aa2018aa2019';var str2 = 'aa2018aa2019';reg.exec(str); //=> 2018reg.exec(str2); //=> 2019,说明也是从上一次索引开始查找的reg.exec(str); //=> nullreg.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 // 1matches.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; //=> 0reg.exec(str); //=> ['18']reg.lastIndex; //=> 0reg.exec(str); //=> ['18']//=> 手动改变 lastIndex 值,不起作用reg.lastIndex = 4;reg.lastIndex; //=> 4reg.exec(str); //=> ['18']//=> 需要使用全局匹配let str = 'aa18bb19';let reg2 = /\d+/g;reg2.lastIndex; //=> 0reg2.exec(str); //=> ['18']reg2.lastIndex; //=> 4reg2.exec(str); //=> ['19']reg2.lastIndex; //=> 8reg2.exec(str); //=> nullreg2.lastIndex; //=> 0reg2.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'
