用大白话轻松搞定正则(下)

Symbol卢
• 阅读 1664

前言

本文是 轻松搞定js正则上 的一篇续文,建议大家在看本篇文章之前,先看 轻松搞定js正则上,(正则基础好和大佬可以忽略),文章中可能有不足的地方,也希望大家可以在评论区提出来,大家一起学习(本人菜的抠脚),这篇文章也是断断续续的接近半个月才写完(主要也是因为忙,后来又遇见了双节的技术正文,于是就又先写了一起学重绘和回流 这篇征文),最后大家一起来聊聊这篇水文吧!如果这篇文章对您有帮助,可以关注我的公众号,咱们一起学前端😄

选择匹配

选择匹配类似 JavaScript 的逻辑运算中的 || 表示 的意思,在js正则中 ,有一个匹配模式叫做 选择匹配

使用的是 | ,它的语法就是 x|y 先上一个简单的案例:

let str='abcd';
let reg=/a|ab/;
let res=str.match(reg);
console.log(res)//["a", index: 0, input: "abcd", groups: undefined] 

选择匹配 次序是从左到右,直到发现了匹配项,就忽略右边的匹配项(即使它产生了更好的匹配)。

上面的匹配即便ab更合适但还是只会匹配a

再上个🌰,我们想使用选择匹配来匹配一段字符,只能是ab 或者 cd。上代码:

let reg=/^ab|cd$/;
console.log(reg.test('ab'));//true   ab或者是cd中的一个,所以为true
console.log(reg.test('cd'));//true   ab或者是cd中的一个,所以为true
console.log(reg.test('abc'));//true  以ab开头,所以为 true
console.log(reg.test('abd'));//true  以 a开头,中间b或者c,以d结尾,所以为true
console.log(reg.test('acd'));//true  以 a开头,中间b或者c,以d结尾,所以为true
console.log(reg.test('abcd'));//true 以ab开头 或者 以cd结尾 ,所以为true
console.log(reg.test('bcd'));//true  以cd 结尾,所以为true
console.log(reg.test('bacd'));//true 以cd 结尾,所以为true 

上面的输出的结果是不是很意外,我们想要的结果就是 ab 或者 cd 会输出 true ,其他的则是false,导致上面出现的原因,就是优先级的问题。

直接使用 x|y 也就是选择匹配,如果进行选择匹配的字符在 两个以及两个以上 , 会存在很乱的优先级的问题,正则里面内部到底是怎么样的运算规则会出现这样的问题,笔者目前也还没有搞清楚(希望能够和各位大佬在评论区讨论学习),所以在使用选择匹配的时候一般不单独的使用,通常在使用选择匹配的时候,都会使用小括号 进行一个分组的,小括号可以改变优先级,具体怎么怎么改变,咱夹着往下看哈 用大白话轻松搞定正则(下)

分组匹配

上面咱们也聊了,可以使用分组用来更改 正则匹配的时候的 优先级,在大家上小学的时候就知道 ,在进行算数运算的时候 可以使用 ()来提高计算的优先级,同样在正则中,我们也是可以使用 () 来进行分组匹配来提高匹配的优先级

这是一个 小数的数学运算题
1+2+(2*3)
=1+2+6
=3+6
=9 

接着咱来看看使用分组匹配来改变选择匹配的优先级问题

let reg=/^(ab|cd)$/;
console.log(reg.test('ab'));//true
console.log(reg.test('cd'));//true
console.log(reg.test('abc'));//false
console.log(reg.test('abd'));//false
console.log(reg.test('abcd'));//false
console.log(reg.test('bcd'));//false
console.log(reg.test('bacd'));//false 

在代码中,使用 () 进行分组,改变优先级,先匹配 ab|cd ,然后才是 ^$

分组的捕获和引用

被正则表达式捕获(也就是匹配)到的字符串会被暂存起来,这些被捕获到的字符串,会从左到右 开始 第一个分组捕获到的字符就会 被标记为 1 开始进行编号,后面的一次类推,

let str = '2020-09-29';
let reg = /(\d{4})-(\d{2})-(\d{2})/;
let res=reg.test(str);  
console.log(res);//true
console.log("第一个分组匹配到的字符",RegExp.$1);
console.log("第二个分组匹配到的字符",RegExp.$2);
console.log("第三个分组匹配到的字符",RegExp.$3); 

用大白话轻松搞定正则(下)

🌰使用正则中的分组来做一个创建的日期格式替换的Demo

聊到替换,大家肯定会联想到咱们在上一篇文章中所聊到的 replace() 方法,下面是使用它结合 分组,实现日期替换的两种方式:👇👇👇

方法一:

let str = '现在北京时间2020:09:29';
let reg = /(\d{4}):(\d{2}):(\d{2})/;
str = str.replace(reg,'$1-$2-$3') 
console.log(str)//现在北京时间2020-09-29 

方法二:

let str = '现在北京时间2020:09:29';
let reg=/:/g;
str = str.replace(reg,'-');
console.log(str)//现在北京时间2020-09-29 

着两种方式看似都可以实现 上面的功能,但是我会选择第一种方案进行使用 ,你知道答案是什么嘛??

原因很简单,因为第一种方法 的正则中/(\d{4}):(\d{2}):(\d{2})/ 匹配到的是我们常见的事件格式,即使是在一个很长的字符串中,能够被它捕获到的也只有这样的事件格式了,而第二种方法中的 正则/:/g 可以捕获到的则是字符中中所有的: ,而且还都会被替换成 -,而在一个很长的字符中中,用到 字符: 的地方有很多,都会被替换成-

命名捕获分组

命名捕获分组,用大白话来说就是,对捕获分组进行一个命名,它的一个语法就是 (?<name>...) ,也就是比咱们上面聊的分组中的普通分组多了 ?<name> ,其实这里面的 name 就和咱们平常取的变量名一样。

let str="现在是北京时间2020-09-30";
let reg=/(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/;
//exec() 和 match() 方法返回的匹配结果数组上多了一个 groups 属性,里面存放着每个命名分组的名称以及它们匹配到的值,
let res=str.match(reg).groups;
console.log(res); 

用大白话轻松搞定正则(下)

可以通过 解构赋值 获取到匹配到是 年月日,需要将日期换成哪种格式,再做一个字符串的拼接就可以啦😄

分组匹配不捕获

(?:) 只匹配不捕获;

有的时候只是为了分组并不需要捕获的情况下就可以使用非捕获型分组,例如:

let str = '2020-09-29';
let reg = /(?:\d{4})-(\d{2})-(\d{2})/
console.log(reg.test(str));
console.log("RegExp.$1:",RegExp.$1);
console.log("RegExp.$2:",RegExp.$2); 

用大白话轻松搞定正则(下)

在字符中2020-09-29 中 使用正则 /(?:\d{4})-(\d{2})-(\d{2})/ 进行匹配,reg.test(str) 打印的结果为 true ,说明了 字符中是满足正则的匹配条件的,RegExp.$1 打印的结果是 09,是因为第一个分组 (?:\d{4}) 只进行了匹配,没有进行捕获。

正向预查(前瞻)

正向预查(也叫做 前瞻),其中包含了 正向肯定预查正向否定预查 两种方式。这里大家先记住即可,后面会详细聊的

正向肯定预查

正向肯定预查 它的一个语法就是 (?=pattern) 其中这里面的字符 pattern 表示的就是 要获取到这个字符,但是不匹配出来 (仅仅作为匹配的时候的条件限制,不会把匹配的结果精输出),是不是感觉有点绕,不好理解,咱直接上代码聊

let reg=/\d+(?=元)/g;
console.log(reg.test("399元"));//true
console.log("399元".match(reg))//399 

在代码中 /\d+(?=元)/g 匹配的是 一次或多次的数字,后面 肯定 跟着一个,格式为:1234元(即数字后面必须是元),但是正则捕获的只是前面的数字,预查的 是不捕获的

let reg=/\d+(?=元)/g;
console.log(reg.test("399元"));//true
console.log("399元".match(reg))//399
console.log(reg.test("499磅"));//false 
console.log("499磅".match(reg))//null 

在代码中 /\d+(?=元)/g 正向肯定(括号中的?后面是=)预查的是,所以 字符串 399元 符合了正则表达式匹配的规则,即 reg.test("399元") 的结果是 true , "399元".match(reg) 的结果是399 ;而在代码 reg.test("499磅") 中,数字后面的结尾是 字,不符合 /\d+(?=元)/g 预查的 ,所以 reg.test("499磅") 的结果为 false, "499磅".match(reg) 的结果为 null ,即没有匹配的结果。

正向否定预查

正向否定预查,刚好是和正向肯定预查 是对立,正向肯定预查是 (?=pattern) 那正向否定预查就是 (?!pattern),前面聊到了 /\d+(?=元)/g 匹配的是 数字后面肯定是 ,那 /\d+(?!元)/g 匹配的是什么哪,别着急,往下看哈;

let reg=/\d+(?!元)/g;
console.log("399元".match(reg))//39
console.log("3990元".match(reg))//399
console.log("499磅".match(reg))//499
console.log("599¥".match(reg))//599 

(?!元) 是正向否定预查 这个字符 (不是 元 这个字符),那 /\d+(?!元)/g 可以捕获到的就是,数字后面不是元的字符 ;所以是不是觉得上面的代码中的第4行和第5行的代码的结果在你的预料之中,但是第二行和第三行的结果是不是有点不太理解,为什么第二行的结果是 39,而不是399,因为如果是 399的话,后面的数字就是,这里是否定预查,数字后面的字符,不是,所以能够捕获到的就是 39,它后面的字符是 9 ,不是

有了正向预查的基础,接下来再聊聊反向预查⬇⬇⬇⬇⬇⬇⬇

反向预查(后顾)

在上面,咱们聊到了正向预查也叫做前瞻,大家可以结合上面的案例代码,仔细的揣摩一下,首先不管是正向预查还是反向预查(后顾),它们都是在匹配的时候作为一个条件的限制,不会把匹配的结果进行输出。在正向肯定预查的案例代码中,/\d+(?=元)/g 是以 这个汉字 作为分界点, 这个汉字的 前面 是数字,也就是 数字的后面肯定是 ,同样正向否定预查也是这个道理。到这里是不是对正向预查又理解的更深一步了呢!!(这段文字放在这里主要是能够让大家去对比着去理解正向预查和反向预查)

反向预查从它的名字上也看的出来,它和正向预查是相反的,反向预查也叫做后顾,同样它也有反向肯定预查反向否定预查两种方式;

反向肯定预查

反向肯定预查 它的一个语法是 (?<=pattern) 其中这里面的字符 pattern 表示的就是 要获取到这个字符,但是不匹配出来 (仅仅作为匹配的时候的条件限制,不会把匹配的结果精输出)

let reg=/(?<=¥)\d+/g;
console.log("¥399".match(reg))//399
console.log("¥3990".match(reg))//3990
console.log("$499".match(reg))//null 

也就是说,数字前面 一定 这个字符,但是字符 是只获取,不会把匹配的结果进行输出,所以第二行的代码输出的是399,第三行的代码输出的是3990,第四行中的字符$499 数字前面不是 ,即不满足匹配条件(数字前面的字符必须是的数字)所以没有匹配到结果。

反向否定预查

反向否定预查 它的一个语法是 (?<!pattern) 其中这里面的字符 pattern 表示的就是 要获取到这个字符,但是不匹配出来 (仅仅作为匹配的时候的条件限制,不会把匹配的结果精输出)

let reg=/(?<!¥)\d+/g;
console.log("¥399".match(reg))//99
console.log("¥3990".match(reg))//990
console.log("$499".match(reg))//499 

正则/(?<!¥)\d+/g 获取数字的前面,不是 的数字,第二行中的代码 "¥399".match(reg) 获取的是数字的前面是不 的数字,(有点绕,仔细揣摩一下),99的前面是 数字3 ,不是 符合匹配的条件,所以输出的结果是99, 第三行也是同样的道理,第四行的代码中"$499".match(reg) 中,数字499前面的字符是$ ,不是 ,所以满足匹配条件的是499

贪婪匹配与非贪婪匹配

难点:贪婪模式/惰性模式(非贪婪)

贪婪模式——在匹配成功的前提下,尽可能多的去匹配(它比较贪心,想要的更多😄)

惰性模式——在匹配成功的前提下,尽可能少的去匹配(它比较懒惰,尽可能的少匹配罒ω罒)

// 贪婪匹配
let greedy = 'aaaaaa'.match(/a+/g);
console.log('greedy',greedy );
// 非贪婪匹配
let lazy = 'aaaaaa'.match(/a+?/g);
console.log('lazy',lazy); 

用大白话轻松搞定正则(下)

在上面的代码中,'aaaaaa'.match(/a+/g) 就是一个贪婪的,其中正则/a+/g 是在全局中,匹配字符a 一次或多次 ,在匹配成功的前提下,尽可能的去多匹配,一直匹配到无法匹配为止,所以匹配到的结果就是["aaaaaa"] , 然而 'aaaaaa'.match(/a+?/g) 就是 一个非贪婪的(也就是 懒惰的),虽然只是多了一个 ?,但是匹配的结果却有很大的不同,? 表示的是 零次或一次 , 在贪婪模式的后面加上? 就变成了懒惰,也就是会尽可能去少匹配,即在匹配的过程中,一旦匹配到结果就会终止匹配(因为它是懒惰的罒ω罒)。

历史文章

  1. 一起学重绘和回流 | 掘金技术征文-双节特别篇
  2. 用大白话轻松搞定正则(上)
  3. js垃圾回收机制原理给你聊的明明白白
  4. ES11来了,不进来看看嘛
  5. 秒懂js作用域与作用域链
  6. 你需要的git命令大全来了
点赞
收藏
评论区
推荐文章
blmius blmius
3年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
Symbol卢 Symbol卢
3年前
一起学重绘和回流
前言Hello,大家好,我是Symbol卢,最近也比较忙,原本打算继续更新的文章,收到了大家的反馈帮助到了一些同学(一同学习的朋友),也是真的很开心😄但是刚好遇见这次的征文由于某些原因就先写了这篇文章(偷偷的说:“用大白话轻松搞定正则的下也快写完了”),因为我的成长道路上也有很多的前辈给我很大的帮助,所以我会继续的传承下去;重绘和回流也是面试当中经常
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
待兔 待兔
5个月前
手写Java HashMap源码
HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22
Jacquelyn38 Jacquelyn38
3年前
2020年前端实用代码段,为你的工作保驾护航
有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )
Stella981 Stella981
3年前
KVM调整cpu和内存
一.修改kvm虚拟机的配置1、virsheditcentos7找到“memory”和“vcpu”标签,将<namecentos7</name<uuid2220a6d1a36a4fbb8523e078b3dfe795</uuid
Wesley13 Wesley13
3年前
mysql设置时区
mysql设置时区mysql\_query("SETtime\_zone'8:00'")ordie('时区设置失败,请联系管理员!');中国在东8区所以加8方法二:selectcount(user\_id)asdevice,CONVERT\_TZ(FROM\_UNIXTIME(reg\_time),'08:00','0
Stella981 Stella981
3年前
Docker 部署SpringBoot项目不香吗?
  公众号改版后文章乱序推荐,希望你可以点击上方“Java进阶架构师”,点击右上角,将我们设为★“星标”!这样才不会错过每日进阶架构文章呀。  !(http://dingyue.ws.126.net/2020/0920/b00fbfc7j00qgy5xy002kd200qo00hsg00it00cj.jpg)  2
Wesley13 Wesley13
3年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
Python进阶者 Python进阶者
11个月前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这