关于正则表达式的方法属性分为两种,一个是RegExp对象方法,其中包含exec和test方法,另外一个是string对象方法,包含match,search,replace,split,这些string方法在原本的基础上还支持正则表达式,扩充了更为强大的能力。下面我将分别总结各个方法的特性和示例以供参考总结。
RegExp对象属性
- global是否全文搜索,默认false。
- ignore case是否大小写敏感,默认是false。
- multiline多行搜索,默认值是false。
- lastIndex是当前表达式匹配内容的最后一个字符的下一个位置。
- source正则表达式的文本字符串。
let reg1 = /\d/;
let reg2 = /\d/gim;
reg1.global; //false
reg1.ignoreCase; //false
reg1.multiline; //false
reg2.global; //true
reg2.ignoreCase; //true
reg2.multiline; //true
RegExp对象方法
RegExp.prototype.exec(string)
功能说明:该函数通过对指定你的字符串进行一次匹配检测,获取字符串中的第一个与正则表达式的内容,并且将匹配的内容和子匹配的结果存放在返回数组中
基本方法:RegExp.exec(string)
exec() 方法用于检索字符串中的正则表达式的匹配。返回值是一个数组,但是此数组的内容和正则对象是否是全局匹配有着很大关系
- 没有g修饰符:
在非全局匹配模式下,只能够在字符串中匹配一次,如果没有找到匹配的字符串,那么返回null,否则将返回一个数组,数组的第0个元素存储的是匹配字符串,第1个元素存放的是第一个引用型分组(子表达式)匹配的字符串,第2个元素存放的是第二个引用型分组(子表达式)匹配的字符串,依次类推。同时此数组还包括两个对象属性,index属性声明的是匹配字符串的起始字符在要匹配的完整字符串中的位置,input属性声明的是对要匹配的完整字符串的引用。
let str = 'zhaodao'
let reg = /(a)o/ // 将a用括号起来是为了演示返回数组的第二个元素特意加上的
let ret = reg.exec(str); //ret是结果数组
console.log(ret);
// ["ao", "a", index: 2, input: "zhaodao", groups: undefined]
返回数组是有以下元素组成的
- 第一个元素(“ao”)是与正则表达式相匹配的文本
- 第二个元素是(“a”)reg对象的第一个子表达式相匹配的文本(如果有的话)。
- index 匹配到的字符位于原始字符串的基于0的索引值 (2)
- input 原始字符串(zhaodao)
- groups 具名组对象,ES2018 引入了 具名组匹配
- 具有g修饰符:
在全局匹配模式下,此函数返回值同样是一个数组,并且也只能够在字符串中匹配一次。不过此时,此函数一般会和lastIndex属性匹配使用,此函数会在lastIndex属性指定的字符处开始检索字符串,当exec()找到与表达式相匹配的字符串时,在匹配后,它将lastIndex 属性设置为匹配字符串的最后一个字符的下一个位置。可以通过反复调用exec()函数遍历字符串中的所有匹配,当exec()函数再也找不到匹配的文本时,它将返回null,并把lastIndex 属性重置为0。
数组的内容结构和没有g修饰符时完全相同。
let str = 'zhaodao'
let reg = /\wao/g
let ret
while ((ret = reg.exec(str)) !== null) {
console.log(reg.lastIndex, ret.index, ret)
}
// 4 1 ["hao", index: 1, input: "zhaodao", groups: undefined]
// 7 4 ["dao", index: 4, input: "zhaodao", groups: undefined]
// lastIndex 属性设置为匹配字符串的最后一个字符的下一个位置,第一次是4第二次是7
注意:不要把正则表达式字面量(或者RegExp构造器)放在 while 条件表达式里。由于每次迭代时 lastIndex 的属性都被重置,如果匹配,将会造成一个死循环。并且要确保使用了’g’标记来进行全局的匹配,否则同样会造成死循环。
RegExp.prototype.test()
功能说明:test() 方法执行一个检索,用来查看正则表达式与指定的字符串是否匹配。返回 true 或 false。
基本方法:RegExp.test(string)
let reg1 = /\w/;
reg1.test('a'); //true
reg1.test('#'); //false
上面的正则匹配就是很常用的用法,匹配返回true,不匹配则返回false,但是当正则表达式加上g标志后,再进行多次匹配后就会有些区别。
let reg = /ao/g;
// 第一遍
reg.test('zhaodao'); //true
console.log(reg.lastIndex) // 4
// 第二遍
reg.test('zhaodao'); //true
console.log(reg.lastIndex) // 7
// 第三遍
reg.test('zhaodao'); //false
console.log(reg.lastIndex) // 0
// 第四遍
reg.test('zhaodao'); //true
console.log(reg.lastIndex) // 4
// 第五遍
reg.test('zhaodao'); //true
console.log(reg.lastIndex) // 7
// 第六遍
reg.test('zhaodao'); //false
console.log(reg.lastIndex) // 0
通过上面的代码我们发现同样的正则匹配在执行到第三和第六遍的时候,结果却返回false。实际上这是因为RegExp.lastIndex。lastIndex是正则表达式的一个可读可写的整型属性,用来指定下一次匹配的起始索引。每次匹配到之后,lastIndex会改变,就会把lastIndex指向匹配到的字符串后一个字符的索引。reg.lastIndex初始时为0,第一个次匹配到a的时候,reg.lastIndex为1。第二次匹配到b的时候,reg.lastIndex为2,匹配不到符合正则的字符串之后,lastIndex会变为0。所以,这就是为什么reg.test(‘zhaodao’)再多次执行之后,返回值为false的原因了。
let reg = /ao/g ;
reg.lastIndex = 7;
reg.test('zhaodao'); //false
上述例子,一开始从位置2开始匹配,位置2后面没有符合正则的字符串,所以为false。
String对象方法
String.prototype.search()
功能说明:search() 方法执行了一个在给定字符串中的一个搜索以取得匹配正则模式的项,如果成功匹配则返回该正则模式的第一个匹配项的在字符串中的位置索引,搜索不到返回-1。
基本方法:stringObject.search(regExp)
let reg1 = /ao/
let reg2 = /ao/g
console.log('zhaodao'.search(reg1)) // 2
console.log('zhaodao'.search(reg2)) // 2
search() 方法不执行全局匹配,它将忽略标志 g。它同时忽略 regexp 的 lastIndex 属性,并且总是从字符串的开始进行检索,这意味着它总是返回 stringObject 的第一个匹配的位置。
String.prototype.match()
功能说明:match() 方法可在字符串内检索指定的值,或找到一个或多个正则表达式的匹配。
基本方法:stringObject.match(regExp)
match方法的返回值是依赖传入的正则是否包含 g ,如果没有 g 标识,那么 match 方法对 string 做一次匹配,如果没有找到任何匹配的文本时,match 会返回 null ,否则,会返回一个数组,数组第 0 个元素包含匹配到的文本,其余元素放的是正则捕获的文本,数组还包含两个对象,index 表示匹配文本在字符串中的位置,input 表示被解析的原始字符串。如果有 g 标识,则返回一个数组,包含每一次的匹配结果。
非全局调用的情况
如果RegExp没有g标志,那么match只能在字符串中,执行一次匹配。
如果没有找到任何匹配文本,将返回null。
否则将返回一个数组,其中存放了与它找到的匹配文本有关的信息。
let reg = /ao(\d+)(.\w*)/
let ts = 'zhaodao88.com'
let res = ts.match(reg)
let res2 = reg.exec(ts)
console.log(res) // ["ao88.com", "88", ".com", index: 5, input: "zhaodao88.com", groups: undefined]
console.log(res2) // ["ao88.com", "88", ".com", index: 5, input: "zhaodao88.com", groups: undefined]
// 'ao88.com' 是整个匹配。
// '88' 被'(\d+)'捕获。
// '.com' 是被'(.\w*)'捕获。
// 'index' 属性(5) 是整个匹配从零开始的索引。
// 'input' 属性是被解析的原始字符串。
非全局情况下和RegExp.prototype.exec()方法的效果是一样的
全局调用的情况
let reg = /\d\w\d/g
let ts = '1a2b3c4e'
let res = ts.match(reg)
console.log(res)
// ["1a2", "3c4"]
全局情况下于RegExp.prototype.exec()的区别是没有了分组信息,而且match方法的执行效率要更快,也不需要使用循环来逐个获取所有的匹配项。
String.prototype.split()
功能说明:split() 方法切割 String 对象为一个其子字符串的数组。
基本方法:stringObject.split(regExp, limit)。
第一个是用来分割字符串的字符或者正则,如果是空字符串则会将元字符串中的每个字符以数组形式返回,第二个参数可选作该参数可指定返回的数组的最大长度,如果设置了该参数,返回的子串不会多于这个参数指定的数组,如果没有设置该参数,整个字符串都会被分割,不考虑它的长度。
let ts = 'a1b2c3d'
let res1 = ts.split(/\d/)
let res2 = ts.split(/[a-z]/)
console.log(res1)
console.log(res2)
// ["a", "b", "c", "d"]
// ["", "1", "2", "3", ""]
let str = 'a1bb2ccc3dddd'
let res3 = str.split(/\d/)
let res4 = str.split(/\d/, 2)
console.log(res3)
console.log(res4)
// ["a", "bb", "ccc", "dddd"]
// ["a", "bb"] 这里设置了第二个参数为2 ,所以返回前两个值(数组的长度为2)
String.prototype.replace()
功能说明:replace() 方法会在一个字符串中用给定的替换器,替换所有符合正则模式的匹配项,并返回替换后的新字符串结果。用来替换的参数可以是一个字符串或是一个针对每次匹配的回调函数。
基本方法:stringObject.replace(regexp|substr, newSubStr|function)
第一个参数可以是字符串或正则表达式,如果提供的是字符串,只会替换第一个子字符串。如果想替换所有子字符串,需要提供一个指定了 g 的正则表达式。
第二个参数可以是字符串或函数。
如果是字符串,可以使用一些特殊的 字符序列:
字符序列 | 替换文本 |
---|---|
$& | 匹配整个模式的子串。 |
$` | 当前匹配的子串 左边 的内容 |
$’ | 当前匹配的子串 右边 的内容 |
$n | n 取值为 1 - 99。 $n 其实等价于 RegExp.prototype.exec() 返回的数组,但不同点在于, $0 是无效的,得使用 $&。n 超出了捕获组的索引范围,会替换为 “$n”(即失去特殊转换的效果)。 |
基本用法
let ts = 'zhaodao88'
let res1 = ts.replace(/ao/, '66')
let res2 = ts.replace(/ao/g, '66')
console.log(res1) // zh66dao88
console.log(res2) // zh66d6688
匹配整个模式的子串
let str = 'abc'
let res1 = str.replace(/b/g, '$&') //$& 代表匹配整个模式的子串即为 字符串b 等同于 str.replace(/b/g, 'b')
let res2 = str.replace(/b/g, '$&,c') // 等同于 str.replace(/b/g, 'b,c')
console.log(res1, res2)
// abc ab,cc
匹配的子串 左边 的内容
let str = 'abc'
let res = str.replace(/b/g, '$`') // $`代表匹配子串b左边的的内容即为a, 等同于str.replace(/b/g, 'a')
console.log(res)
// aac
匹配的子串 右边 的内容
let str = 'abc'
let res = str.replace(/b/g, '$'') // $'代表匹配子串b右边的的内容即为c, 等同于str.replace(/b/g, 'c')
console.log(res)
// acc
$n 的用法
let str = '++alibaba-baidu-csdn++'
let res = str.replace(/(\w+)-(\w+)-(\w+)/g, '$3_$2_$1') // $1代表匹配的第一个子串是 alibaba,$2代表baidu,$3代表csdn
console.log(res)
// ++csdn_baidu_alibaba++
如果第二个参数也可以是函数,这个函数接收多个参数
function (match, [p1, p2, ..., pn], offset, string)
变量名 | 含义 |
---|---|
match | 匹配的子串, 对应于上述的$&。 |
p1,p2,… | 对应于上述的$1,$2等。 |
offset | 匹配到的子字符串在原字符串中的偏移量。 |
string | 被匹配的原字符串。 |
let str = '++alibaba-baidu-csdn++'
let res = str.replace(/(\w+)-(\w+)-(\w+)/g, function (match, p1, p2, p3, offset, string) {
console.log(match) // alibaba-baidu-csdn
console.log(p1) // alibaba
console.log(p2) // baidu
console.log(p3) // csdn
console.log(offset) // 2
console.log(string) // ++alibaba-baidu-csdn++
return `${p3}_${p2}_${p1}`
})
console.log(res) // ++csdn_baidu_alibaba++
等价于
let str = '++alibaba-baidu-csdn++'
let reg = /(\w+)-(\w+)-(\w+)/g
let res = str.replace(reg, function () {
return `${RegExp.$3}_${RegExp.$2}_${RegExp.$1}`
})
贪婪与非贪婪
- 贪婪匹配:尽可能匹配最长的字符串
- 非贪婪匹配: 尽可能匹配最短的字符串
当正则表达式中包含能接受重复的限定符时,通常的行为是匹配尽可能多的字符。以这个表达式为例
let str = 'aabaaaab'
let reg = /a{2,}/g
console.log(str.match(reg)) // ['aa', 'aaaa']
有时,我们更需要懒惰匹配,也就是匹配尽可能少的字符。前面给出的限定符都可以被转化为懒惰匹配模式,只要在它后面加上一个问号?。类似于这样
let str = 'aabaaaab'
let reg = /a{2,}?/g
console.log(str.match(reg)) // ['aa', 'aa', 'aa']
贪婪匹配转为非贪婪,只要在正则量词后加上?。 正则量词:(*、+、?、{n,m}、{n,})
引用
https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Guide/Regular_Expressions
https://juejin.im/post/6844903494906806279#heading-11