深刻学习正则表达式

正则里括号的用法

1. 分组

分组:正则表达式里括号的表达式为另一组匹配规则正则表达式

捕获括号:被匹配的子字符串能够在结果数组的元素 [1]-[n] 中找到,或在被定义的 RegExp 对象的属性 $1-$9 中找到。数组

代码举例:bash

let reg = /\d+(\D+)/
reg.exec('123456abcd')
// ["123456abcd", "abcd", index: 0, input: "123456abcd", groups: undefined]
console.log(RegExp.$1)
// "abcd"
复制代码

在这个正则表达式里咱们括号指望的是一组非数字的匹配项,而且执行匹配后可在执行结果的[1]或者RegExp.$1获得匹配值。工具

正则表达式括号的分组在实际开发中对于咱们解决问题有很是大的用处,例如String.replace()这个方法学习

代码举例:开发工具

let str = '123abc'
let reg = /(\d+)(\D+)/
let newStr = str.replace(reg, '$2$1')
console.log(newStr) // abc123
str.replace(reg, function(word, $1, $2){
	console.log(word,$1,$2)
	// word表明字符串在正则匹配到的值, $1表明第一个括号的匹配项, $2表明第二个括号的匹配项
	// 123abc, 123, abc
})
复制代码

使用正则表达式应用于字符串处理,在上面的例子里咱们很容易得就把数字和字母的匹配项互换位置。ui

在另一种状况下若是不想要捕获这个匹配项,可是又须要加括号匹配条件,咱们可使用非捕获括号spa

非捕获括号:匹配项不可以从结果数组的元素 [1]-[n] 或已被定义的 RegExp 对象的属性 $1-$9 再次访问到。code

代码举例:对象

let reg = /\d+(?:\D+)/
reg.exec('123456abcd')
// ["123456abcd", index: 0, input: "123456abcd", groups: undefined]
复制代码

在例子里执行匹配后括号里的匹配项不会再出现结果里。

2.反向引用

反向引用:一个反向引用(back reference),指向正则表达式中第 n 个括号(从左开始数)中匹配的子字符串。

代码举例:

reg = /(\d+)\D+\1/
reg.exec('123abc123')
// ["123abc123", "123", index: 0, input: "123abc123", groups: undefined]
复制代码

在正则表达式里\1表明的是\d+,当咱们在表达式里有须要重复的时候能够用这种写法。

3.零宽断言

零宽断言:指一个用来描述或者匹配一系列符合某个句法规则的字符串的单个字符串。

  1. (?=pattern) 正向先行断言:表明字符串中的一个位置,紧接该位置以后的字符序列可以匹配pattern。
  2. (?!pattern) 负向先行断言:表明字符串中的一个位置,紧接该位置以后的字符序列不能匹配pattern。
  3. (?<=pattern) 正向后行断言:表明字符串中的一个位置,紧接该位置以前的字符序列可以匹配pattern。
  4. (?<!pattern) 负向后行断言:表明字符串中的一个位置,紧接该位置以前的字符序列不能匹配pattern。

正则表达式的括号有时候用来表达断言,具体的细节咱们在下面问内容详细说。

贪婪模式与非贪婪模式

贪婪模式与非贪婪模式也是正则里面比较常见的问题了,平时也会常常应用于开发中解决问题。理解贪婪模式和非贪婪模式对咱们理解正则引擎执行匹配很是有帮助。

贪婪模式

贪婪模式会匹配尽量多的字符,贪婪模式用于匹配优先量词修饰的子表达式,匹配优先量词包括:“{m,n}”、“{m,}”、“?”、“*”和“+”

代码举例:

let reg = /\d*/
reg.exec('1234567890')
["1234567890", index: 0, input: "1234567890", groups: undefined]
复制代码

*号表明匹配任意次数,用大括号表明即{0,},在贪婪模式下尽量多的匹配,在例子中由于整个字符串彻底匹配,因此匹配值为 1234567890。

非贪婪模式

非贪婪模式会匹配尽量少的字符,在匹配量词后面加上问号就可触发非贪婪模式:“{m,n}?”、“{m,}?”、“??”、“*?”和“+?”

代码举例:

let reg = /\d*?/
reg.exec('1234567890')
// ["", index: 0, input: "1234567890", groups: undefined]
复制代码

*号表明匹配任意次数,用大括号表明即{0,},由于*号可表明匹配0次,在非贪婪模式下尽量少的匹配,因此在这个例子里匹配项为空,即不匹配任何字符串。

零宽断言

正则表达式的断言功能很是强大,学习正则的断言应用,对于解决咱们开发中的问题提供了新的思路。

在理解断言的执行过程可能会稍微有点绕,可是做为一个开发确定要有一颗爱折腾的心,哈哈。

下面将只使用正向先行断言来讲明断言的执行,其余的三个模式也是大同小异。

先看一个简单例子:

let reg = /abc(?=123)/
reg.exec('abc123')
// ["abc", index: 0, input: "abc123", groups: undefined]

let reg2 = /abc(?=1234)/
reg2.exec('abc123')
// null

let reg3 = /abc(?=12)/
reg3.exec('abc123')
// ["abc", index: 0, input: "abc123", groups: undefined]
复制代码

先按照正则的字面意思理解,/abc(?=123)/指望的匹配为即匹配abc,且abc后面的字符串可以知足括号的匹配规则,注意的是括号里面能够为其余正则表达式,并非说abc后面只能包含123,而是后面能够知足括号的匹配则为断言成功。

在reg2的匹配过程当中,由于abc后面的字符串不知足括号的匹配规则,因此断言失败,执行匹配也失败了。

在这几个例子里尚未体现出咱们概念里说的意思,重温一下正向先行断言的概念

(?=pattern) 正向先行断言:表明字符串中的一个位置,紧接该位置以后的字符序列可以匹配pattern

概念里说的意思断言是在字符串中寻找符合断言的一个位置

举例说明:

let reg = /(?=abc).*/
reg.exec('123abc123')
// ["abc123", index: 3, input: "123abc123", groups: undefined]
复制代码

先分析正则表达式,在知足abc匹配条件的位置后面匹配任意字符。在这个例子里,存在abc知足断言的匹配规则,可是为何匹配到的是abc123?

在这里就回到咱们的标题,零宽断言,零宽的意思就是执行断言是不会消耗咱们正则表达式在匹配过程当中的字符串,而且,断言是在帮咱们肯定符合断言匹配规则的位置。因此,(?=abc)会帮咱们肯定一个断言成功的位置,即3和a之间的位置,而后在这个断言成功的位置开始执行匹配(.*)。

let reg = /(?=abc)\d+/
reg.exec('123abc123')
// null
复制代码

在上面的例子中,虽然abc的断言成功,可是断言只是帮咱们肯定一个位置,而后再执行\d+匹配规则,由于断言是不会消耗字符串,因此实际上以abc123去和\d+匹配,最后匹配结果为null。

基于此咱们可使用断言帮咱们从一开始检索整个字符串是否知足某些规则,有助于提高匹配效率。

以下例子,咱们可使用断言从一开始判断整个字符串是否所有由数字组成,若是断言失败,则不执行匹配,这对于咱们应用于表单校验很是有助于提高效率。

reg = /(?=^\d+$)\d+/
reg.exec('123456')  // 123456
reg.exec('123456a') // null
复制代码

另外没有介绍到的三种模式也是大同小异,在这里也就不重复赘述。可是两种后行断言可能会存在兼容性问题,后行断言应该是ES2018新增的规范。

零宽断言的重点是要理解“零宽”以及“位置”这两个点。

最后总结一下:正则表达式是一门很是实用的工具语言,基本上只要学习了就可以对于咱们实际开发中产生帮助,平时某些开发工具中也可使用正则表达式去检索某些文档,对于提高效率真的是帮助很是大。

相关文章
相关标签/搜索