JavaScript正则表达式的模式匹配教程,并且附带充足的实战代码

str.match(pattern) //返回 null

/----------------完全的重复一遍子匹配模式----------------/

let pattern = /java(\d+)(\d+)/

let str = “java123321”

str.match(pattern) //返回 [‘java123321’, ‘12332’, ‘1’]

通过这两个例子的对比,我们可以发现以下几点区别:

  1. 子匹配模式必须和反斜杠+数字 匹配到的字符串一模一样,否则匹配失败

  2. 两个相同的子匹配模式则不需要两者匹配到一模一样的字符串

  3. 反斜杠+数字 虽然是对定义的子匹配模式的引用,但在匹配返回的结果里,却不会返回 反斜杠+数字 匹配到的内容

补充:如果我们用小括号定义的子匹配模式不想被反斜杠+数字引用,我们可以在小括号内部的最前面加上 ?:,即这种形式 (?:\d+) ,这样的话我们就无法在后面使用 反斜杠+数字 来引用这个子匹配模式了。

例如:

let pattern = /java(?:script)(\d+)\1/

let str = “javascript1212”

str.match(pattern) //返回 [‘javascript1212’, ‘12’]

例子中我们可以看到, \1 是对第二个子匹配模式(\d+)进行了引用,其实我们可以这样理解,使用这种形式(?:...)定义的子匹配模式,不会被计入编号中,所以也不会被 反斜杠+数字 引用。

指定匹配位置

在正则表达式中,我可以利用某些字符,去指定匹配发生的位置。这些字符我们称之为正则表达式的

| 字符 | 含义 |

| — | — |

| ^ | 匹配字符串的开头 |

| $ | 匹配字符串的结尾 |

| \b | 匹配一个单词的边界 |

| \B | 匹配非单词边界的位置 |

| (?=p) | 零宽正向先行断言,?=后面的字符都要与p匹配,但不能包括p的那些字符 |

| (?!p) | 零宽负向先行断言,?!后面的字符不与p匹配 |

我们来逐个说一下这几个字符的用法:

  • ^ 符号

^ 这个符号是将匹配位置定位到字符串的开头,直接来看一个例子

/--------------------------第一种情况--------------------/

let pattern = /^javascript/

let str = “javascript is fun”

str.match(pattern) //返回 [‘javascript’]

/--------------------------第二种情况--------------------/

let pattern = /^javascript/

let str = “i love javascript”

str.match(pattern) //返回 null

我们匹配的模式是,要以javascript开头的字符串。第一种情况,字符串以 javascript开头,所以能匹配到;第二种情况,javascript不是在开头的位置,而是在末尾的位置,不符合匹配模式,所以匹配失败返回null。

在前面我们有一个地方还用到了 ^ 这个符号,那就是 [^abc] ,所以一定要注意,当 ^ 放在方括号里,表示的是取反,也就是说不匹配方括号里的任何字符。

  • $ 符号

$ 这个符号是将匹配位置定位到字符串的末尾,直接来看一个例子

/--------------------------第一种情况--------------------/

let pattern = /javascript$/

let str = “javascript is fun”

str.match(pattern) //返回 null

/--------------------------第二种情况--------------------/

let pattern = /javascript$/

let str = “i love javascript”

str.match(pattern) //返回 [‘javascript’]

我们的匹配模式是,字符串要以javascript结尾。第一种情况,字符串结尾处字符是 fun ,不符合匹配模式,返回null;第二种情况,结尾处字符为javascript,符合匹配模式,所以匹配成功。

我们可以看一下如果 ^ 符号 和 $ 符号一起使用是什么情况:

let pattern = /^javascript$/

let str = “javascript”

str.match(pattern) //返回 [“javascript”]

当这两个符号一起使用时,匹配模式就变成了匹配整段字符串,并且字符串的内容就是 ^ 与 $ 之间的内容

  • \b

这个符号的作用是匹配一个单词的边界,我们来看几个例子来理解一下

/-------------------------第一种情况----------------------/

let pattern = /\bjava/

let str = “I love javascript”

str.match(pattern) // 返回 [‘java’] 匹配成功

/-------------------------第二种情况----------------------/

let pattern = /\bjava/

let str = “javascript is fun”

str.match(pattern) // 返回 [‘java’] 匹配成功

/-------------------------第三种情况----------------------/

let pattern = /\bjava/

let str = “1javascript is fun”

str.match(pattern) // 返回 null 匹配失败

/-------------------------第四种情况----------------------/

let pattern = /java\b/

let str = “I am learning java”

str.match(pattern) // 返回 [‘java’] 匹配成功

/-------------------------第五种情况----------------------/

let pattern = /java\b/

let str = “I am learning javascript”

str.match(pattern) // 返回 null 匹配失败

/-------------------------第六种情况----------------------/

let pattern = /\bjava\b/

let str = “I think java is fun”

str.match(pattern) // 返回 [‘java’] 匹配成功

看了上面几个例子,你有没有得出什么规律?

其实 \b 的作用就是将匹配的点放到一个字符串前面(\b放前面)或后面(\b放后面)的 [^a-zA-Z0-9] 处,也可以理解为 \b 可以替换那些特殊字符,但 \b 不会作为匹配的内容。

  • \B

\B 则与 \b 相反了, \b 可以替换那些特殊字符,那么\B就是用来替换那些非特殊字符,也就是 [a-zA-Z0-9] 内的字符。

也来举几个简单的例子吧

/-------------------------第一种情况----------------------/

let pattern = /java\B/

let str = “I love javascript”

str.match(pattern) // 返回 [‘java’] 匹配成功

/-------------------------第二种情况----------------------/

let pattern = /java\B/

let str = “I love java”

str.match(pattern) // 返回 null 匹配失败

/-------------------------第三种情况----------------------/

let pattern = /\Bjava\B/

let str = “I love 1javascript”

str.match(pattern) // 返回 [‘java’] 匹配成功

/-------------------------第四种情况----------------------/

let pattern = /\Bjava\B/

let str = “I love javascript”

str.match(pattern) // 返回 null 匹配失败

  • (?=p)

(?=p)表示接下来的字符要与p匹配,但p不会作为内容返回

先来看一个例子

let pattern = /java(script)?(?=:)/

let str = “java: my favorite language”

str.match(pattern) //返回 [“java”, undefined] 匹配成功

该例子的匹配模式:匹配一段字符串为java 然后 script 可以有一个也可以没有,后面必须跟一个 :,才能匹配成功,返回匹配内容,但是匹配内容中不包含 :

再来看一个类似的例子

let pattern = /java(script)?(?=:)/

let str = “javascript is my favorite language”

str.match(pattern) //返回 null 匹配失败

该例子匹配失败是因为字符串中的javascript后面没有 :,没有满足 (?=\:)的匹配要求

  • (?!p)

(?!p)与(?=p)相反,它表示接下来的字符不与p匹配

我们也来看一个例子

let pattern = /java(?!script)/

let str = “javascript is my favorite language”

str.match(pattern) //返回 null 匹配失败

该例子的匹配模式是:匹配一段字符,只要java后面紧跟这的字符不是script即为匹配成功,同时java后面跟的内容也不会作为内容返回。但这个例子中的 str 里, java后面紧跟着的就是script,所以匹配失败。那么成功的例子就是这样的

let pattern = /java(?!script)/

let str = “javascr1111 is my favorite language”

str.match(pattern) //返回 [“java”] 匹配成功

在该例子中, java 后面跟着的就不是script了,所以匹配成功了。java后面的字符不作为内容返回,所以最后的匹配结果就是 java

修饰符

正则表达式的修饰符是用以说明高级匹配模式的规则,并且修饰符是放在// 双斜杠外面的,例如这样 /java/g,g就是修饰符

接下来给出一张表,列出了修饰符的种类和含义

| 字符 | 含义 |

| — | — |

| i | 执行不区分大小写的匹配 |

| g | 执行全局匹配,即找到所有匹配的项并返回,而不是找到第一个之后就停止 |

| m | 多行匹配模式 |

我们来逐个讲解它们各自的用途:

  • 字符 i

我们之前会用这样的匹配模式去匹配 /[Jj]ava[Ss]cript/,这是因为我们不知道字符串中写的是 javascript 还是 JavaScript。其实我们可以直接用一个修饰符 i 来避免大小写的干扰,就想这样 /javascript/i

let pattern = /javascript/i

let str = “JavaScript”

str.match(pattern) // 返回 [‘JavaScript’] 匹配成功

我们可以看到,匹配时忽略了大小写的影响,仍然匹配成功了

  • 字符 g

我们之前匹配字符串时,都是匹配到第一个就结束匹配返回内容,例如

let pattern = /java/

let str = “I love javascript and java”

str.match(pattern) //返回 [“java”] 匹配到javascript的java就返回了

我们可以看到 str 字符串中,有两个java,但只匹配到javascript就返回了,如果我们要匹配到字符串中所有符合匹配模式的字符串,我们就可以用修饰符g,就像这样 /java/g

let pattern = /java/g

let str = “I love javascript and java”

str.match(pattern) //返回 [“java”, “java”] 匹配到了所有的java

  • 字符 m

如果一个字符串中包含换行符,则该字符串就有多行。这时我们可以使用修饰符 m 进行多行模式的匹配。

let pattern = /java$/m

let str = “java\nis fun”

str.match(pattern) //返回 [‘java’] 匹配成功

在这个例子中,str内有一个换行符,这样的话,第一行就是 java ,第二行就是 is fun 。我们的匹配模式是查找每一行,只要这一行的结尾是java,就返回匹配到的内容。

其实在这个例子中,我们看到,使用了修饰符 m 以后,锚字符 ^ 和 $ 不再是以一整个字符串的开头或结尾为匹配点了,而是以每一行的开头或结尾为匹配点。

(3)用于模式匹配字符串的方法

我们在前面的很多例子中用到了search()方法 、match()方法,他们都是匹配字符串的方法,其实还有很多种匹配的方法,他们的用法和作用各不相同,我们来了解一下

用于模式匹配字符串的方法有以下几种:

  1. search( )

  2. replace( )

  3. match( )

  4. split()

接下来我们来详细讲解一下

search()

该方法需要传入一个正则表达式作为参数,并返回第一个与之匹配的字符串的起始位置,若没匹配到,则返回-1

“javascript”.search(/script/) //返回 4

“javascript”.search(/sccc/) //返回-1

replace()

该方法是用于字符串的检索与替换。需要传入两个参数,第一个参数为正则表达式;第二个参数为需要进行替换的字符串。匹配成功则会用第二个参数去替换匹配到的字符串,并返回替换后的整体字符串;若没匹配成功,则返回原来的整体字符串。

“javascript”.replace(/java/, ‘python’) //返回 pythonscript

“javascript”.replace(/abc/, ‘python’) //返回 javascript

若使用了修饰符g,则会将所有匹配到的字符串都进行一个替换。

“javascript and java”.replace(/java/, ‘python’) //返回 pythonscript and java

“javascript”.replace(/java/g, ‘python’) //返回 pythonscript and python

match()

该方法需要传入一个正则表达式作为参数,返回一个由匹配结果组成的数组,如果正则表达式使用了修饰符g,则将所有匹配到的结果都放到数组中并返回。

“javascript and java”.match(/java/) //返回 [‘java’]

“javascript and java”.match(/java/g) //返回 [‘java’, ‘java’]

补充:当match()传入一个非全局的正则表达式时,实际上返回的数组里有两个属性:index和input,他们分别表示匹配到的字符串的起始位置和检索的整个字符串。

let ret = “I love javascript and java”.match(/java/)

ret.index //返回 7

ret.input //返回 I love javascript and java

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’]

四、new RegExp()


在第三部分讲解正则表达式时,我们都是用的直接量的形式创建的RegExp对象,其实使用直接量,在内部还是会调用RegExp()构造函数去创建对象实例的,那我们就来看看直接通过RexExp()构造函数是如何创建对象的吧。

RegExp()构造函数一共有两个参数,第一个参数为正则表达式的主体部分;第二个参数是可选的,为修饰符。需要注意的是,我们在写主体部分的时候,有些地方要用一个反斜杠\进行转义,我们必须将一个反斜杠\ 替换成两个反斜杠 \\。接下来我们来看一下一个简单的例子:

//创建一个RegExp对象,全局匹配字符串中连着的三个数字

let pattern = new RegExp(“\d{3}”, “g”)

这种创建RegExp对象的方法有一个好处,就是可以动态的改变正则表达式的主体部分;而RegExp直接量就无法做到动态变化。

RegExp对象的属性

我们创建的每个RegExp对象(包括RegExp直接量)都包含有5个属性:

  1. source:这是一个只读属性,包含正则表达式的文本,例如 /java/的source表示的就是 java

  2. global:这是一个只读的布尔值,用以表示这个正则表达式是否使用了修饰符 g

  3. ignoreCase:这是一个只读的布尔值,用以表示这个正则表达式是否使用了修饰符 i

  4. multiline:这是一个只读的布尔值,用以表示这个正则表达式是否使用了修饰符 m

  5. lastIndex:这是一个可读写的整数值,如果匹配模式中有修饰符 g,则这个属性会存储下一次检索的开始位置,这个属性只有在调用exec()和test()两个方法的时候会用到。

RegExp对象的方法

RegExp对象定义了两个用于匹配的方法——exec()test(),这两个方法与我们之前讲到的用于模式匹配字符串的方法不同的是,前者是定义在RegExp对象上的方法,并且传入的参数是字符串;而后者是调用字符串的方法,传入的参数是RegExp对象。

  • exec()

该方法就跟前面说到的不传入修饰符g的matach()方法一样,它对字符串执行一个正则表达式,如果匹配失败,返回null;如果匹配成功,则返回一个数组,数组的第一个元素是正则表达式匹配到的字符串,剩下的元素则是子表达式匹配到的字符串,同时该数组也包含indexinput两个属性。

来看一个例子,体会一下exec()方法的作用

let pattern = new RegExp(“java”, “g”)

let ret = pattern.exec(“I love javascript and java”) //返回[“java”]

ret.index //返回 7

ret.input //返回 I love javascript and java

exec()match() 方法不同的是,不管正则表达式是否使用修饰符g,exec()都只会将第一个匹配到的字符串以及子表达式匹配到的字符串放到数组里返回;而match()方法在没有使用修饰符g时,跟exec()一样,如果使用了修饰符g,则将所有匹配到的字符串都放在数组里一起返回,并且不会返回圆括号里匹配到的字符串,同时,该数组里不包含indexinput两个属性。

那么这里引发一个疑问,既然不管是否使用修饰符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

从上面这个例子我们能看看到,lastIndex默认为0,表示从字符串的开头开始检索,当我们进行第一次检索时,匹配到了javascript中的java,返回了该字符串,这时lastIndex变为第一次匹配到的字符串的起始位置索引 4;我们进行第二次索引,是从索引 5 开始匹配的,往后检索到字符串末尾的java,并返回该字符串,同时lastIndex变为第二次匹配到的字符串起始位置索引 19;我们进行第三次索引,是从索引 20 开始匹配的,往后匹配,已经无法匹配到对应的字符串了,所以返回一个null,并将lastIndex重置为0,表示下一次检索又从字符串的开头开始检索。

  • test()

该方法与exec()类似,该方法也是传入一个字符串作为参数,对该字符串进行检索,如果匹配到了相应的字符串,则返回true;如果没匹配到,则返回false。

let pattern = new RegExp(“java”, “g”)

let ret = pattern.test(“I love javascript and java”) //返回 true

test() 与 exec() 相同的是,如果匹配时使用了修饰符g,test()也会根据对象内部属性 lastIndex 进行检索匹配,这里就不多做说明了。

五、实战应用


(1)判断电话号码格式

应用场景:判断用户输入的电话号码格式是否正确

电话号码格式:1开头,后面跟10位数字

let str = “12356456132” //str为用户输入的电话号码

let re = /^1\d{10}$/

if(re.test(str) !== true) {

console.log(‘电话号码格式不正确’)

}

else {

console.log(‘电话号码格式正确’)

}

(2)判断邮箱格式

应用场景:判断用户输入的邮箱格式是否正确

邮箱格式:第一部分@第二部分,第一部分由字母、数字 和 短横线 - 组成,第二部分由(字母、数字、短横线 - ).cn.com…… 组成

let str = “561325647@qq.com” //用户输入的邮箱

let re = /1+@(\w-?)+(.\w{2,})+$/

if(re.test(str) !== true) {

console.log(‘邮箱格式不正确’)

}

else {

console.log(‘邮箱格式正确’)

}

(3)判断昵称格式

应用场景:判断用户输入的昵称是否符合规定格式

昵称格式:昵称由字母、数字、下划线组成,必须由字母开头,长度为4-16位

let str = “Lpyexplore233” //用户输入的昵称

let re = /2[\w_]{3,15}$/

if(re.test(str) !== true) {

最后

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数初中级Android工程师,想要提升技能,往往是自己摸索成长,自己不成体系的自学效果低效漫长且无助。

因此收集整理了一份《2024年Web前端开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。

img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点!不论你是刚入门Android开发的新手,还是希望在技术上不断提升的资深开发者,这些资料都将为你打开新的学习之门!

如果你觉得这些内容对你有帮助,需要这份全套学习资料的朋友可以戳我获取!!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!
线 - ).cn.com…… 组成

let str = “561325647@qq.com” //用户输入的邮箱

let re = /3+@(\w-?)+(.\w{2,})+$/

if(re.test(str) !== true) {

console.log(‘邮箱格式不正确’)

}

else {

console.log(‘邮箱格式正确’)

}

(3)判断昵称格式

应用场景:判断用户输入的昵称是否符合规定格式

昵称格式:昵称由字母、数字、下划线组成,必须由字母开头,长度为4-16位

let str = “Lpyexplore233” //用户输入的昵称

let re = /4[\w_]{3,15}$/

if(re.test(str) !== true) {

最后

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数初中级Android工程师,想要提升技能,往往是自己摸索成长,自己不成体系的自学效果低效漫长且无助。

因此收集整理了一份《2024年Web前端开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。

[外链图片转存中…(img-6ajtArXb-1715489378608)]

[外链图片转存中…(img-NhA84Pf1-1715489378609)]

[外链图片转存中…(img-UppyNbbq-1715489378609)]

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点!不论你是刚入门Android开发的新手,还是希望在技术上不断提升的资深开发者,这些资料都将为你打开新的学习之门!

如果你觉得这些内容对你有帮助,需要这份全套学习资料的朋友可以戳我获取!!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!


  1. \w-. ↩︎

  2. a-zA-Z ↩︎

  3. \w-. ↩︎

  4. a-zA-Z ↩︎

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值