重复
上一篇我们讲到了字符类,本文就从重复开始讲。
在上一部分,我们知道字符类都是匹配一个字符,例如 /\d/
和 /[0-9]/
都是匹配任意一个数字 、/[abcd]/
也是匹配一个字符,那如果我们想要匹配多个字符串岂不是要写很多遍重复代码?例如我们要匹配一个三位数字的字符串,我们就需要设置这样一个匹配模式 /\d\d\d/
。其实正则表达式有几种语法,可以将该表达方式简化,我们来看一下这个表格
字符 |
匹配 |
{n, m} |
匹配前一项n-m次 |
{n, } |
匹配前一项至少n次 |
{n} |
匹配前一项n次 |
? |
匹配前一项0次或1次,相当于{0,1} |
+ |
匹配前一项至少一次,相当于{1,} |
* |
匹配前一项0次或更多次,相当于{0,} |
我们接下来就利用这些语法进行一下重复操作,例如我们要匹配一段字符串中的11位数字,我们可以这样写 /\d{11}/
,因为\d
表示的是任意数字,在它后面加上一个重复语法,就是重复\d
多少次。我们如果要匹配一个三位的字母并且后面跟上一个一位的可选数字,我们可以这样 /[a-zA-Z]{3}\d?/
,[a-zA-Z]{3}
表示匹配任意三位字母,\d?
表示匹配数字0次或1次
重复是贪婪的,它们会尽可能的多地匹配,我们称之为贪婪的重复,下面来看一个例子
let pattern = /\d{3,10}/let str = "0123456789" str.match(pattern)[0] //返回 0123456789
这里介绍一个新的匹配方法 match
,它可以将匹配到的一段字符串放在一个数组里,若没匹配到则返回null。 在这个例子中,我们设置的匹配模式是/\d{3,10}/
,表示匹配数字3到10次,因为重复语法默认是贪婪的,它会尽可能多地匹配,所以他就匹配了任意数字10次,返回 0123456789
。
那么如果我们如何使他们不贪婪地重复呢?其实很简单,我们只需要在重复的语法后面加一个 ?
即可将重复变成非贪婪的,还是这个例子
let pattern = /\d{3,10}?/let str = "0123456789" str.match(pattern)[0] //返回 012
这是我们可以看到,在重复语法 {3,10}
后面加上一个 ?
以后,它并没有尽可能多地匹配了,而是变成了尽可能少地匹配,即匹配三次任意数字就结束匹配。
还有其他的非贪婪重复的语法有: ??
、+?
、*?
,你们可以下去自行测试
选择
在JavaScript中有一个运算符可以用在正则表达式中,那就是 |
,它的意思就是或者,例如这个例子 /[a-z]|[0-9]/
意思就是可以匹配任意一个a-z的字母,或者也可以匹配任意一个0-9的数字。
在复杂的例子里,我们也可以这样使用,先给出需求,匹配一段字符串,它可以是3位的不区分大小写的字母,也可以是4位的数字
let pattern = /[a-zA-Z]{3}|\d{4}/let str = "Lpyexplore2333" str.match(pattern)[0] //返回 Lpy
在这个例子中,我们匹配的模式是3位的不区分大小写的字母或者4位数字,但是 str 中既有3位的不区分大小写的字母,也有4位数字,为什么最后只是返回了Lpy呢?因为正则的匹配是从字符串的最左边开始匹配,只要有一个符合匹配模式的就停止匹配。
分组和引用
上面我们说过,在正则表达式中小括号是有特殊含义的,如果真的想要匹配带有小括号的字符串,必须要用反斜杠转移,接下来我们就来介绍一下 ()
小括号的几种作用。
1. 作用一:把匹配模式中的部分项组合成子表达式
类似于这样 /java(script)?/
,这种匹配模式的意思就是,匹配一段为 java
或者 javascript
的字符串。我们可以试一下,如果去掉这个括号会是什么结果,即 /javascript?/
,这种匹配模式的意思就是,匹配一段为 javascrip
或者 javascript
的字符串。
所以我们可以很清楚的知道,()
小括号可以帮我们组合一个子表达式,然后将这个子表达式作为整体,配合 |
、*
、?
、+
等符号去处理。
2. 作用二:定义一个子匹配模式,方便获取子匹配模式匹配到的字符串
在将这个作用前,我还是再来详细介绍一下我之前例子中用到的匹配方法 match()
的具体用法。
match() 方法需要传入一个正则表达式,然后根据这个参数去匹配字符串,最后返回一个数组,数组的第一个元素是该参数匹配到的字符串,数组的第二个元素是该正则表达式中第一个()
小括号内匹配到的字符串,数组的第三个元素是该正则表达式中第二个()
小括号内匹配到的字符串,这样以此类推。若没匹配到就返回null
介绍完 match() 方法的使用以后,我们来看一个例子
/*---------------------在匹配模式中加小括号--------------*/ let pattern = /java(script\d+)/let str = "javascript2333" str.match(pattern) //返回 ['javascript2333', 'script2333'] /*---------------------不在匹配模式中加小括号--------------*/ let pattern = /javascript\d+/let str = "javascript2333" str.match(pattern) //返回 ['javascript2333']
我们可以看到,在匹配模式中加了小括号,最后返回的数组中会额外返回一个元素,用于存放小括号定义的子匹配模式匹配到的字符串。
接下来举一个实战中的例子
有这样一个 url 地址 https://www.baidu.com/s?query=javascript
,我们知道 ?
后面跟的是请求参数,如果我们想要获取请求参数 query 的值,也就是 query=
后面的字符串,我们该如何使用正则表达式去匹配呢?
let pattern = /query=([a-zA-Z]+)/let str = "https://www.baidu.com/s?query=javascript" str.match(pattern) //返回 ['query=javascript', 'javascript']
在这个例子中,我们很明确的知道我们只是想获取 query=
后面的字符串,但是如果我们直接用这个模式 /query=[a-zA-Z]+/
去匹配的话,我们最后只能获得 query=javascript
这样一整段字符串。所以我们可以在我们可以使用小括号来定义一个子匹配模式,这样在返回的数组中直接获取小括号匹配返回的值就可以了。
3. 作用三:小括号定义的子匹配模式可以被反斜杠+数字再次引用
其实作用三是在作用二的基础上的,我们可以通过一个反斜杠 \
加上数字 n来引用该匹配模式中第n个括号定义的子匹配模式,例如 /java(script)\1/
,这个意思就是 \1
的部分需要匹配的字符串要跟(script)
一样
let pattern = /java(\d+)\1/let str = "java123123" str.match(pattern) //返回 ['java123123', '123']
在这个例子中,\1
对(\d+)
进行了一次引用,注意是引用,而不是这样 /java(\d+)(\d+)/
。我们来看一下这两者的区别
/*----------------使用反斜杠加数字引用----------------*/ let pattern = /java(\d+)\1/let str = "java123321" str.match(pattern) //返回 null /*----------------完全的重复一遍子匹配模式----------------*/ let pattern = /java(\d+)(\d+)/let str = "java123321" str.match(pattern) //返回 ['java123321', '12332', '1']
通过这两个例子的对比,我们可以发现以下几点区别:
子匹配模式
必须和反斜杠+数字
匹配到的字符串一模一样,否则匹配失败
- 两个相同的子匹配模式则不需要两者匹配到一模一样的字符串
反斜杠+数字
虽然是对定义的子匹配模式的引用,但在匹配返回的结果里,却不会返回反斜杠+数字
匹配到的内容
补充:如果我们用小括号定义的子匹配模式不想被反斜杠+数字
引用,我们可以在小括号内部的最前面加上 ?:
,即这种形式 (?:\d+)
,这样的话我们就无法在后面使用 反斜杠+数字
来引用这个子匹配模式了。
例如:
let pattern = /java(?:script)(\d+)\1/let str = "javascript1212" str.match(pattern) //返回 ['javascript1212', '12']
例子中我们可以看到, \1
是对第二个子匹配模式(\d+)
进行了引用,其实我们可以这样理解,使用这种形式(?:...)
定义的子匹配模式,不会被计入编号中,所以也不会被 反斜杠+数字
引用。
结束语
第二篇对于 JavaScript正则表达式的讲解就先到这里,明天后天会继续更新第三篇,并且我会在之后的文章上放上前几篇的文章链接,方便大家观看,希望大家持续关注,点个关注,不迷路