正则表达式(?=)正向先行断言实战案例
<p>最近在练习正则表达式,遇到了一道很有意思的题,题目如下</p><p style="text-align:center"><img alt="" src="https://img.jbzj.com/file_images/article/202211/202211171016068.png" /></p>
<p>我的答案如下</p>
<div class="jb51code"><pre class="brush:js;">(?=.*?)(?=.*?\d)(?=.*?).{8,}</pre></div>
<p>对于这个答案的理解得先从正向先行断言的语法开始说起。</p>
<p>正向先行断言的语法格式如下</p>
<div class="jb51code"><pre class="brush:js;">expression1(?=expression2)
# 查找expression2前面的expression1</pre></div>
<p>当然这个<strong>expression1</strong>也可以不写(也就是为<strong>空白符</strong>)</p>
<p>例子如下</p>
<p style="text-align:center"><img alt="" src="https://img.jbzj.com/file_images/article/202211/202211171016079.jpg" /></p>
<p>该正则表达式的意思为:寻找<strong>abcd</strong>字符串前的<strong>123456</strong>字符串。</p>
<p>这里也提一个有意思的地方</p>
<p style="text-align:center"><img alt="" src="https://img.jbzj.com/file_images/article/202211/2022111710160710.png" /></p>
<p style="text-align:center"><img alt="" src="https://img.jbzj.com/file_images/article/202211/2022111710160711.png" /></p>
<p>以上两个正则表达式中的<strong>/\d+/gm</strong>和<strong>/123456/gm</strong>其实都能匹配<strong>123456</strong>这个字符串,但在正向先行断言中,前者会匹配每个数字前面的<strong>空白符</strong>,后者将123456字符串当成一个整体,只匹配这个整体前面的<strong>空白符。</strong></p>
<p>这里面的原理还需要等我研究一下,估计是跟底层代码的实现有关,我猜测是<strong>(?=\d+)</strong>在匹配的时候会将每个数字单独提取出然后向前比较。</p>
<p>那么回到该题的答案中,先让我们看看 <strong>(?=.*?) </strong>是什么意思。</p>
<p style="text-align:center"><img alt="" src="https://img.jbzj.com/file_images/article/202211/2022111710160712.png" /></p>
<p>很明显上图匹配了大写字母<strong>A</strong>前面的所有<strong>空白符</strong></p>
<p>其中的<strong>.*?</strong>代表大写字母及其前面的字符串且为懒惰匹配</p>
<p style="text-align:center"><img alt="" src="https://img.jbzj.com/file_images/article/202211/2022111710160713.png" /></p>
<p>那么<strong>(?=.*?)(?=.*?\d)</strong>的意思就有点套娃了,按我的理解就是对于<strong>(?=.*?\d)</strong>而言把<strong>(?=.*?)</strong>当成<strong>expression1</strong>,对于<strong>(?=.*?)</strong>而言就是把<strong>空白符</strong>当成<strong>expression1</strong>。</p>
<p>那么这个正则表达式就表示为:在寻找到<strong>每个大写字母前面的所有空白符</strong>的基础上还要满足:这些空白符都在<strong>每个数字前面的所有空白符</strong>这个匹配集合中。相当于是<strong>两个空白符集合的交集</strong>。</p>
<p>所以<strong>(?=.*?)(?=.*?\d)(?=.*?)</strong>相当于是<strong>每个大写字母、小写字母、数字前面的所有空白字符的交集</strong>。</p>
<p style="text-align:center"><img alt="" src="https://img.jbzj.com/file_images/article/202211/2022111710160714.png" /></p>
<p> 而后面的<strong>.{8,}</strong>则匹配这些空白字符后面<strong>至少八位字符(贪婪匹配)</strong>。</p>
<p style="text-align:center"><img alt="" src="https://img.jbzj.com/file_images/article/202211/2022111710160715.jpg" /></p>
<p><strong>附:先行否定断言</strong></p>
<p>x(?!y)称为先行否定断言(Negative look-ahead),x只有不在y前面才匹配,y不会被计入返回结果。比如,要匹配后面跟的不是百</p>
<p>分号的数字,就要写成/\d+(?!%)/。</p>
<div class="jb51code"><pre class="brush:js;">/\d+(?!\.)/.exec('3.14') // ["14"]
// ["14"]
</pre></div>
<p>上面代码中,正则表达式指定,只有不在小数点前面的数字才会被匹配,因此返回的结果就是14。</p>
<p>“先行否定断言”中,括号里的部分是不会返回的。</p>
<div class="jb51code"><pre class="brush:js;">var m = 'abd'.match(/b(?!c)/);
m // ['b']
</pre></div>
<p>上面的代码使用了先行否定断言,b不在c前面所以被匹配,而且括号对应的d不会被返回。</p>
<p><strong>总结</strong></p>
頁:
[1]