一、定义
正则表达式(RegExp)是一个描述字符模式的对象。 通俗点来说就是从一堆字符串中,找到与该模式匹配的字符串,并可以完成检索或字符串替换的功能。
二、学习前需掌握的几个知识点
1、几个常用方法
(1)字符串方法
① search():检测字符串中的子串,并返回子串的起始位置索引,不存在返回-1(string方法)。例如:
const a = /javascript/
const b = "hello javascript"
console.log(b.search(a)) // 返回 6
② match():检测字符串的子串,传入一个正则表达式,然后根据这个参数去匹配字符串,返回一个数组。数组的第一个元素是该参数匹配到的字符串,数组的第二个元素是该正则表达式中第一个 () 小括号内匹配到的字符串(后面讲到小括号的时候会例举),数组的第三个元素是该正则表达式中第二个 () 小括号内匹配到的字符串,这样以此类推。若没匹配到就返回null(string方法)。例如:
// 注意:以下
// b{m, n}:表示b至少出现m次,至多出现n次
// g是正则的一个修饰符。表示全局匹配,即在目标字符串中按顺序找到满足匹配模式的所有子串,强调的是“所有”,而不只是“第一个”。g是单词global的首字母。
const reg = /ab{2, 5}c/g
const str = "abc abbc abbbc abbbbc abbbbbc abbbbbbc"
console.log(str.match(reg)) // 返回 ["abbc", "abbbc", "abbbbc", "abbbbbc"]
③ replace():用于字符串的检索与替换。需要传入两个参数,第一个参数为正则表达式;第二个参数为需要进行替换的字符串。匹配成功则会用第二个参数去替换匹配到的字符串,并返回替换后的整体字符串;若没匹配成功,则返回原来的整体字符串。例如:
"javascript".replace(/java/, 'python') // 返回 pythonscript
"javascript".replace(/abc/, 'python') // 返回 javascript
如果使用了修饰符 g
,则会将所有匹配到的字符串都进行一个替换。
"javascript and java".replace(/java/, 'python') //返回 pythonscript and java
"javascript and java".replace(/java/g, 'python') //返回 pythonscript and python
④ split():用于将字符串分割,并将分割开的部分作为数组中的元素,返回一个数组。该方法需要传入一个正则表达式作为参数,去确定需要根据什么去分割这串字符串,若匹配成功,返回一个数组,数组中的元素就是每个被分割的字符串;若匹配失败,也会返回一个数组,数组中只有一个元素,那就是这个字符串整体。例如:
'1,2,3,4,5,6'.split(/,/) //返回 ['1', '2', '3', '4', '5', '6']
'1,2,3,4,5,6'.split(/\+/) //返回 ['1,2,3,4,5,6']
(2)new RegExp()
正则表达式有两种创建方式:第一种是RegExp直接量
;第二种是new RegExp()
。
使用直接量的原理是内部会调用RegExp()构造函数去创建对象实例。
RegExp() 构造函数一共有两个参数,第一个参数为正则表达式的主体部分;第二个参数是可选的,为修饰符。需要注意的是,我们在写主体部分的时候,有些地方要用一个反斜杠 \ 进行转义,我们必须将一个反斜杠 \ 替换成两个反斜杠 \。例如:
//创建一个RegExp对象,全局匹配字符串中连着的三个数字
const pattern = new RegExp("\\d{3}", "g")
这种创建RegExp对象的方法有一个好处,就是可以动态的改变正则表达式的主体部分;而RegExp直接量就无法做到动态变化。
① RegExp对象的属性
source
:这是一个只读属性,包含正则表达式的文本,例如 /java/ 的 source 表示的就是 java。global
:这是一个只读的布尔值,用以表示这个正则表达式是否使用了修饰符g
。ignoreCase
:这是一个只读的布尔值,用以表示这个正则表达式是否使用了修饰符i
。multiline
:这是一个只读的布尔值,用以表示这个正则表达式是否使用了修饰符m
。lastIndex
:这是一个可读写的整数值,如果匹配模式中有修饰符g
,则这个属性会存储下一次检索的开始位置,这个属性只有在调用exec()
和test()
两个方法的时候会用到。
② RegExp对象的方法
exec()
:此方法和不传入修饰符 g 的matach()
方法一样,它对字符串执行一个正则表达式,如果匹配失败,返回null;如果匹配成功,则返回一个数组,数组的第一个元素是正则表达式匹配到的字符串,剩下的元素则是子表达式匹配到的字符串,同时该数组也包含index和input两个属性。例如:
let pattern = new RegExp("java", "g")
let ret = pattern.exec("I love javascript and java") //返回["java"]
console.log(ret.index) // 返回 7
console.log(ret.input) // 返回 I love javascript and java
exec()
与 match()
方法不同的是,不管正则表达式是否使用修饰符 g,exec() 都只会将第一个匹配到的字符串以及子表达式匹配到的字符串放到数组里返回;而 match() 方法在没有使用修饰符 g时,跟 exec() 一样,如果使用了修饰符 g,则将所有匹配到的字符串都放在数组里一起返回,并且不会返回圆括号里匹配到的字符串,同时,该数组里不包含index
和input
两个属性。
既然不管是否使用修饰符 g,exec() 方法都只会返回第一个匹配到的字符串,那这个修饰符 g 有什么用呢? 其实我们在前面有说到,RegExp 对象内有一个属性叫做 lastIndex
,该属性默认为0。当我们调用 exec() 方法,并且使用了修饰符 g 进行匹配时,若匹配成功,lastIndex 将变为下一次检索开始位置的索引值;若匹配失败,lastIndex则重置为0。例如:
let pattern = new RegExp("java", "g")
console.log(pattern.lastIndex) //查看lastIndex默认为0
let str = "I love javascript and java"
pattern.exec(str) //进行第一次检索匹配,返回["java"]
console.log(pattern.lastIndex) //此时lastIndex为 4
pattern.exec(str) //进行第二次检索匹配,返回["java"]
console.log(pattern.lastIndex) //此时lastIndex为 19
pattern.exec(str) //进行第三次检索匹配,返回null
console.log(pattern.lastIndex) //此时lastIndex为 0
test()
:检测字符串中的子串,存在返回 true,不存在返回 false(RegExp方法)。例如:
const a = /javascript/
const b = "java"
console.log(a.test(b)) // 返回 true
2、匹配特殊字符
如 ^
,$
,*
,+
,?
,=
,!
,:
,|
,\
,/
,(
,)
,[
,]
,{
,}
,如需匹配这些字符,则要在前面加上 \ 。例如:
const a = /you\(Amy\)/
const b = "I love you(Amy)"
console.log(b.search(a)) // 返回 7
3、两种模糊匹配
(1)横向模糊匹配
一个正则可匹配的字符串的长度不是固定的。
实现方式为{m,n}
,表示连续出现最少m次,最多n次。例如:
const reg = /ab{2, 5}c/g
const str = "abc abbc abbbc abbbbc abbbbbc abbbbbbc"
console.log(str.match(reg)) // 返回 ["abbc", "abbbc", "abbbbc", "abbbbbc"]
(2)纵向模糊匹配
一个正则匹配的字符串,具体到某一位字符时,它可以不是某个确定的字符。
实现方式为[abc]
,表示该字符是可以字符“a”、“b”、“c”中的任何一个。例如:
const reg = /a[123]b/g
const str = "a0b a1b a2b a3b a4b"
console.log(str.match(reg)) // 返回 ["a1b", "a2b", "a3b"]
4、字符组(字符类)
虽叫字符组(字符类),但只是其中一个字符。例如[abc],表示匹配一个字符,它可以是“a”、“b”、“c”之一。
(1)范围表示法
使用特殊字符 -
。例如:
// 1、数字 1 到 9
const num = /[1-9]/
// 2、小写字母 a 到 z
const low = /[a-z]/
// 3、大些字母 A 到 Z
const up = /[A-Z]/
// 4、混合
const mix = /[1-9a-zA-Z]/
(2)排除字符组
使用特殊字符 ^
。例如:
// 除 1 到 9 的所有字符
const word = /[^1-9]/
(3)简写形式
简写 | 描述 |
---|---|
[…] | 方括号内的任意一个字符 |
[^…] | 不在方括号内的任意一个字符 |
. | 除了换行符和其他 Unicode 行终止符之外的任意字符 |
\w | 相当于 [a-zA-Z0-9] |
\W | 相当于 [^a-zA-Z0-9] |
\s | 任何 Unicod e空白符 |
\S | 任何非 Unicode 空白符 |
\d | 任何数字,相当于 [0-9] |
\D | 任何非数字,相当于 [^0-9] |
[\b] | 退格直接量 |
5、量词
量词也称重复,即连续出现的次数。
(1)简写形式
简写 | 描述 |
---|---|
{m,} | 至少出现 m 次 |
{m} | 等价于{m,m},表示出现 m 次 |
? | 等价于{0,1},表示出现或者不出现 |
+ | 等价于{1,},表示出现至少一次 |
* | 等价于{0,},表示出现任意次,有可能不出现 |
(2)贪婪匹配
重复是贪婪的,它们会尽可能的多地匹配,这就是贪婪匹配。例如:
const reg = /\d{2,5}/g
const str = "123 1234 12345 123456"
console.log(str.match(reg)) // 返回 ["123", "1234", "12345", "12345"]
(3)惰性匹配
尽可能少的匹配,就是惰性匹配。例如:
const reg = /\d{2,5}?/g
const str = "123 1234 12345 123456"
console.log(str.match(reg)) // 返回 ["12", "12", "34", "12", "34", "12", "34", "56"]
6、多选分支
多选分支可以支持多个子模式任选其一。
实现方式为用 |
(管道符)分隔,表示其中任意之一。例如:
const reg = /good|nice/g
const str = "good idea, nice try."
console.log(str.match(reg)) // 返回 ["good", "nice"]
值得注意的是,如果用 /good|goodbye/ 去匹配字符串 “goodbye” 时,结果为 “good”。
const reg = /good|goodbye/g
const str = "goodbye"
console.log(str.match(reg)) // 返回 ["good"]
而把 /good|goodbye/ 改成 /goodbye|good/ 时,结果为 “goodbye”。
const reg = /goodbye|good/g
const str = "goodbye"
console.log(str.match(reg)) // 返回 ["goodbye"]
这表明多选分支是惰性的
,即当前面的匹配上了,后面的就不再尝试了。
7、分组与引用( ()
小括号的作用)
(1)把匹配模式中的部分项组合成子表达式。例如:
const a = /java(script)?/
const b = "I love java"
const c = "I love javascript"
console.log(a.test(b)); // 返回 true
console.log(a.test(c)); // 返回 true
上述正则匹配 java
或 javascript
字符串。如果去掉括号,即 /javascript?/
,则匹配 javascrip
或 javascript
字符串。
(2)定义一个子匹配模式,方便获取子匹配模式匹配到的字符串。
/*-----------------在匹配模式中加小括号--------------*/
const pattern = /java(script\d+)/
const str = "javascript2333"
console.log(str.match(pattern)) // 返回 ['javascript2333', 'script2333']
/*---------------不在匹配模式中加小括号--------------*/
const pattern = /javascript\d+/
const str = "javascript2333"
console.log(str.match(pattern)) // 返回 ['javascript2333']
(3)小括号定义的子匹配模式可以被反斜杠+数字n(\n
)再次引用
通过一个反斜杠 \ 加上数字 n来引用该匹配模式中第n个括号定义的子匹配模式,如 /java(script)\1/
,这个意思就是 \1
的部分需要匹配的字符串要跟(script)
一样。例如:
let pattern = /java(\d+)\1/
let str = "java123123"
console.log(str.match(pattern)) // 返回 ['java123123', '123']
在这个例子中,\1
对(\d+)
进行了一次引用,注意是引用,而不是这样 /java(\d+)(\d+)/
。例如:
/*----------------使用反斜杠加数字引用----------------*/
let pattern = /java(\d+)\1/
let str = "java123321"
console.log(str.match(pattern)) // 返回 null
/*----------------完全的重复一遍子匹配模式----------------*/
let pattern = /java(\d+)(\d+)/
let str = "java123321"
console.log(str.match(pattern)) // 返回 ['java123321', '12332', '1']
这两者的区别:
子匹配模式
必须和反斜杠+数字
匹配到的字符串一模一样,否则匹配失败。- 两个相同的子匹配模式则不需要两者匹配到一模一样的字符串。
反斜杠+数字
虽然是对定义的子匹配模式的引用,但在匹配返回的结果里,却不会返回反斜杠+数字
匹配到的内容。
8、修饰符
正则表达式的修饰符是用以说明高级匹配模式的规则,并且修饰符是放在//
双斜杠外面的,例如这样/java/g
,g
就是修饰符。
字符 | 描述 |
---|---|
i | 执行不区分大小写的匹配 |
g | 执行全局匹配,即找到所有匹配的项并返回,而不是找到第一个之后就停止 |
m | 多行匹配模式 |
(1)i
:执行不区分大小写的匹配。例如:
let pattern = /javascript/i
let str = "JavaScript"
console.log(str.match(pattern)) // 返回 ['JavaScript'] 匹配成功
(2)g
:执行全局匹配。
之前匹配字符串时,都是匹配到第一个就结束匹配返回内容,例如:
let pattern = /java/
let str = "I love javascript and java"
console.log(str.match(pattern)) //返回 ["java"] 匹配到 javascript 的 java 就返回了
加上修饰符 g
后:
let pattern = /java/g
let str = "I love javascript and java"
console.log(str.match(pattern)) //返回 ["java", "java"] 匹配到所有的java
(3)m
:执行多行匹配模式。例如:
let pattern = /java$/m
let str = "java\nis fun"
console.log(str.match(pattern)) // 返回 ['java'] 匹配成功
9、指定匹配位置
在正则表达式中,我们可以利用某些字符,去指定匹配发生的位置。这些字符我们称之为正则表达式的锚。
字符 | 描述 |
---|---|
^ | 匹配字符串的开头 |
$ | 匹配字符串的结尾 |
\b | 匹配一个单词的边界 |
\B | 匹配非单词边界的位置 |
(?=p) | 零宽正向先行断言,?=后面的字符都要与p匹配,但不能包括p的那些字符 |
(?!p) | 零宽负向先行断言,?!后面的字符不与p匹配 |
(1)^
:将匹配位置定位到字符串的开头。例如:
/*--------------------------第一种情况--------------------*/
const pattern = /^javascript/
const str = "javascript is fun"
console.log(str.match(pattern)) // 返回 ['javascript']
/*--------------------------第二种情况--------------------*/
const pattern = /^javascript/
const str = "I love javascript"
console.log(str.match(pattern)) // 返回 null
注意:当 ^ 放在方括号
里,表示的是取反
,也就是说不匹配方括号里的任何字符。
(2)$
:将匹配位置定位到字符串的末尾。例如:
/*--------------------------第一种情况--------------------*/
const pattern = /$javascript/
const str = "javascript is fun"
console.log(str.match(pattern)) // 返回 null
/*--------------------------第二种情况--------------------*/
const pattern = /javascript$/
const str = "I love javascript"
console.log(str.match(pattern)) // 返回 ['javascript']
注意:当 ^
和 $
符号一起使用时,为匹配整段字符串。例如:
const pattern = /^javascript$/
const str = "javascript"
console.log(str.match(pattern)) // 返回 ['javascript']
(3)\b
:匹配一个单词的边界。例如:
/*-------------------------第一种情况----------------------*/
let pattern = /\bjava/
let str = "I love javascript"
console.log(str.match(pattern)) // 返回 ['java'] 匹配成功
/*-------------------------第二种情况----------------------*/
let pattern = /\bjava/
let str = "javascript is fun"
console.log(str.match(pattern)) // 返回 ['java'] 匹配成功
/*-------------------------第三种情况----------------------*/
let pattern = /\bjava/
let str = "1javascript is fun"
console.log(str.match(pattern)) // 返回 null 匹配失败
/*-------------------------第四种情况----------------------*/
let pattern = /java\b/
let str = "I am learning java"
console.log(str.match(pattern)) // 返回 ['java'] 匹配成功
/*-------------------------第五种情况----------------------*/
let pattern = /java\b/
let str = "I am learning javascript"
console.log(str.match(pattern)) // 返回 null 匹配失败
/*-------------------------第六种情况----------------------*/
let pattern = /\bjava\b/
let str = "I think java is fun"
console.log(str.match(pattern)) // 返回 ['java'] 匹配成功
(4)\B
:匹配非单词边界的位置。例如:
/*-------------------------第一种情况----------------------*/
let pattern = /java\B/
let str = "I love javascript"
console.log(str.match(pattern)) // 返回 ['java'] 匹配成功
/*-------------------------第二种情况----------------------*/
let pattern = /java\B/
let str = "I love java"
console.log(str.match(pattern)) // 返回 null 匹配失败
/*-------------------------第三种情况----------------------*/
let pattern = /\Bjava\B/
let str = "I love 1javascript"
console.log(str.match(pattern)) // 返回 ['java'] 匹配成功
/*-------------------------第四种情况----------------------*/
let pattern = /\Bjava\B/
let str = "I love javascript"
console.log(str.match(pattern)) // 返回 null 匹配失败
注意:\B
与 \b
相反, \b
替换特殊字符[^a-zA-Z0-9]
, \B
替换非特殊字符[a-zA-Z0-9]
。
(5)(?=p)
:表示接下来的字符要与 p 匹配,但 p 不会作为内容返回。例如:
let pattern = /java(script)?(?=\:)/
let str = "java: my favorite language"
console.log(str.match(pattern)) // 返回 ["java", undefined] 匹配成功
(6)(?!p)
:表示接下来的字符不与 p 匹配,且非 p 不会作为内容返回。例如:
let pattern = /java(?!script)/
let str = "javascript is my favorite language"
console.log(str.match(pattern)) // 返回 null 匹配失败