theme: channing-cyan
highlight: a11y-dark
本文正在参加「金石计划 . 瓜分6万现金大奖」
正则表达式 高级🛫
前言
推荐先阅读
再阅读本文。
本文主要讲解是关于 正则表达式 中,断言 及 贪婪和惰性
1. 断言
断言,我个人认为是非常强的,非常的 NB
但是,依然记得,在最开始接触 断言,我都不想看,难懂是一方面,再加上网上其他文章的复杂解释,各种名词,各种乱七八糟,差点都给我劝退了。😅
下面我们来理解下 断言
首先 断言 也可以称为 环视 或者 零宽断言
-
记住 这两个词,至关重要
-
环视 的意思,就是 环顾四周,对于 正则表达式 而言,就是
- 在 表达式 中的 某个位置 处,看看左边 ,看看右边
-
零宽断言 先不着急,我会放到下面讲解。
下面放出 百度百科 截图,因为我觉得说的还是很在理的。
意思很明确,就是程序在执行到 断言 位置,就会去进行判断,判断是否符合条件
- 相当于我们执行
js
中遇到if
条件了
好,这就到我们第一个阶段,程序遇到 断言
- 之后进行判断,也就是所谓的 环视 状态
1.1 环视
假设 下面这一段是我们的 正则表达式 (没有任何含义,主要是用来举例)
/ aaaa bbbb cccc dddd eeee ffff gggg /
我们设置 dddd
为我们的 断言 位置
程序执行,遇到 断言,进入 环视 状态,如下图
在 环视 状态时,会 看看左边,看看右边
-
而事实上,则只选择看一个方向
-
要么选择 看左边 的代码
-
要么选择 看右边 的代码
-
由于只能选择一个方向
那么自然引出,两个名词 先行断言 和 后行断言
1.2 先行,后行
从上面我们知道,遇到 断言 处,只能看一个方向,要么 左边,要么 右边
那这里我们定义下
-
当 看左边时,叫 后行 断言
-
而 看右边 时,则叫 先行 断言
但是呢
无论是 先行断言 还是 后行断言,在所谓的 环视 完毕后
都要做出判断,知道它 是否匹配,是否符合条件
而下一个步骤则是,与大多数判断条件相同,还判断是否有 是否取反 的操作
这就引出我们下面的 正向 和 反向 的概念了
1.3 正向,反向
取反 操作,则对应下面的名词
-
有 取反 叫 反向
-
没有 取反 则叫 正向
在了解什么是 取反 后
将 取反 在与上面的 先行断言 和 后行断言 两两结合
就引出我们 断言 的 真正的 四 种情况,
也是我们经常在网上能够看到的名称
1.4 断言 的四种情况
通过上面的概念,再进行 两两结合,则得出下面真正的 断言
有四种
-
正向 先行 断言
- 含义:在 断言 位置处,向 右边 看表达式,匹配成功则成功,匹配失败则失败
-
正向 后行 断言
- 含义:在 断言 位置处,向 左边 看表达式,匹配成功则成功,匹配失败则失败
-
反向 先行 断言
-
含义:在 断言 位置处,向 右边 看表达式
-
匹配 成功,取反操作后,则代表 失败
-
匹配 失败,取反操作后,则代表 成功
-
-
-
反向 后行 断言
-
含义:在 断言 位置处,向 左边 看表达式
-
匹配 成功,取反操作后,则代表 失败
-
匹配 失败,取反操作后,则代表 成功
-
-
至此
四 种 断言 情况下,它们的 含义 被搞清楚了。
可以看到,这些名词真的是一大堆
要是不准备充分,直接上去莽 断言,劝退不知无数次😵
1.5 写法
在上面,终于是把 断言 中,大部分的概念性名词 和 含义,理解透了!
下面进行写法研究
1.5.1 正向,反向 写法
我们写 js
都知道,在 if
条件中
-
相等 用
===
-
不相等 用
!==
而 取反 操作,与上面很类似
-
正向 用
=
-
反向 用
!
1.5.2 先行,后行 写法
-
默认就是 先行,无符号 表示
-
而 后行,则用
<
符号,来表示
1.5.3 断言 四种写法
当然,断言 肯定也是有特殊写法,好让我们从 正则表达式 中一眼就能看出
断言写法: (?xy表达式)
-
x
代表 先行 还是 后行 -
y
代表 正向 还是 反向
故 最终 四大情况的写法如下
- 正向 先行 断言 写法:
(?=表达式)
- 正向 后行 断言 写法:
(?<=表达式)
- 反向 先行 断言 写法:
(?!表达式)
- 反向 后行 断言 写法:
(?<!表达式)
1.6 零宽断言
上面讲解了 断言 的 概念 和 写法
接下来说一下 为什么 断言 也称 零宽断言
零宽 这个名词,又代表了什么含义
1.6.1 案例1
这里举个例子:
const str1 = '我爱喝旺仔牛奶'
const str2 = '我是旺仔米苏呀'
const str3 = '我是旺仔'
const str4 = '我是旺仔哟'
const str5 = '我是米苏'
const str6 = '我是米苏哟'
这里有 6
个字符串
- 需求:我想要找到里面存在
旺仔
两个字的字符串
那你可能会有 N
种方法来处理,这里用 正则方法 简单写下
const reg = /旺仔/g
当然上面这个需求很简单,如果此时,再给你一个新的需求:
- 想要找到里面存在
旺仔
两个字,并且后面也必须跟上米苏
,这两个字 的字符串,才能成功
const reg = /旺仔(?=米苏)/g
因为是看后边,并且确认必须是 米苏
, 所以我们使用的是, 正向先行断言 的方式来处理
可以明显看到,只有 str2
匹配成功,但是你看结果,并没有将 米苏
这两个字匹配出来。
这就是 断言 为什么称为 零宽断言
因为它仅仅只是看,查看是否符合条件,但是呢,它并不会将匹配结果给出来。
补充
其实,上面的案例讲的很清楚了
这里再扩充一下额外的知识点:
好,在你准备干完活,准备下班!
产品:请留步,我想知道 下面这个字符串是否能够匹配成功?
const str8 = '我是旺仔哟,不是米苏哟'
答案是 不成功 的
原因很简单,是因为我们上面 正则表达式的 写法,肯定是 旺仔
后面紧紧跟着 米苏
这两个字,才能成功
而如果想要成功匹配上面这个字符串,
-
也就是只要
旺仔
后面 出现了米苏
这两个字,就能成功则需要修改我们的 正则表达式
const reg = /旺仔(?=.*米苏)/g
-
只需要
米苏
前面添加.*
,它代表的含义为:米苏
前面出现 任意字符出现 零次 或 多次
即可匹配成功!
1.7 总结
最后简单总结下 断言
断言 称为 环视 和 零宽断言
有四种情况 以及 写法
- 正向 先行 断言 写法:
(?=表达式)
- 正向 后行 断言 写法:
(?<=表达式)
- 反向 先行 断言 写法:
(?!表达式)
- 反向 后行 断言 写法:
(?<!表达式)
其实很多场景下,用 断言 来处理特别简单
-
密码强度,必须有 大写字母,小写字母,数字,特殊符号
-
数字格式化(千位分隔符)
1.8 额外补充: ?:
这里再讲一个容易与上面 断言 混淆的,(?:表达式)
首先说明一下,这个不是 断言
而是 分组 中的 非捕获分组,它并不会将匹配的结果保存下来
也就是,
-
我们无法通过
\1
进行 回溯引用 -
也无法通过
$1
来获取值
注意写法,千万别和上面的 断言 混淆了
2. 贪婪 和 惰性
在 手摸手快速入门 正则表达式 (基础)🛫 这篇文章中,我们知道了 正则表达式 中 重复 的方式
而上面所有的 重复,其实都是 贪婪 模式
什么是 贪婪:
- 通过字面意思,我们可以知道,它会尽可能匹配多的值
这里贴一下相关字符
而 贪婪 相对的,则是 惰性,也称为 非贪婪
- 惰性 匹配,则会匹配较少的值
它的写法,则是上面的 所有字符 后面添加 ?
,即可成为 惰性
即:
*?
+?
??
{n}?
{n,}?
{min,max}?
可以看到还是很简单,能够容易就能够理解的
2.1 案例1
这里再举个例子,来深入了解下 贪婪 与 惰性
给定一段字符串,我想要找到 旺仔
与 号
之间的内容
const str = '我是旺仔米苏号,我是0号'
const str1 = '我是旺仔1号,米苏1号'
const str2 = '我是旺仔12号,米苏12号'
const str3 = '我是旺仔123号,米苏123号'
-
贪婪
const reg = /旺仔(.*)号/g
上面这种写法就是 贪婪 模式,可以看到没有
?
出现,来看下得到的结果可以看到 贪婪 模式下,会匹配到更多值
-
惰性
看下 惰性 能够匹配到的值
const reg = /旺仔(.*?)号/g
这样可以明显看到 它们之间的区别
2.2 案例2
上面的案例,虽然体现了 惰性,但是因为后面有一个 号
的影响,导致它必须匹配到 号
字,才能结束
如果我们把例子修改下,把 号
取消掉,只提取,旺仔
后面的值
则你会发现,惰性 是真的懒🤣
-
贪婪
const reg = /旺仔(.*)/g
可以看到 贪婪 会将后面,所有的值,全部拿到
-
惰性
const reg = /旺仔(.*?)/g
而 惰性,则直接匹配空字符串🤣
2.3 案例3
这里再对上面的案例进行修改,相信这个就能够完全理解了
从 旺仔
开始,到匹配到 数字 后结束
-
贪婪
const reg = /旺仔.*\d/g
可以看到 贪婪 会匹配到更多的数字
-
惰性
const reg = /旺仔.*?\d/g
而 惰性,则直接匹配到数字就结束了!
总结
通过上面 3 个案例,相信可以很清楚的了解了 贪婪 与 惰性 的区别
默认的,基础的 重复 字符 就是 贪婪
而 惰性,则在 基础的 重复 字符的后边,添加 ?
字符,就变为 惰性
暂无评论内容