• (转)教你怎么理解正则表达式之零宽断言(环视)


    原文地址:http://blog.csdn.net/binjly/article/details/12152235

    思考题

    今天有人问一个关于零宽断言的正则:

    [javascript] view plaincopy
     
    1. var reg = /(?=a)b/;  
    2. reg.exec("ab");  

    大家思考下这个正则为什么匹配不了?我们先了解一下什么叫零宽断言,最后再来回答这个问题。

    概念理解

    零宽断言(有的资料叫它环视),分为零宽度正预测先行断言( 格式为 (?=exp) ),和 零宽度正回顾后发断言( 格式为 (?<=exp) )。javascript目前只支持前者,所以这里我们只以前者来讨论。(为了便于表述,下文所出现的“零宽断言”或“断言”如果不特别说明,都指前者。)

    我们先从字面上来理解下。零宽度,说明它是不占字符宽度的,只是一个位置,它不匹配任何东西。但是这个“位置”跟 ^ $  这三个东东是有本质区别的。还是很难理解?好吧,先略过,相信看完本文你会理解的。接下来,正预测,这个很好理解,就是表示这个表达式是一种预测,预测接下来会发生的情况。最后,先行断言,意即提前确定。结合起来,就是:我断言,在我所在的位置后面,必然要出现我所表达的东西

    说了一大堆,很多人还是云里雾里吧,那么,下面这段代码就来加深你的理解:

    [javascript] view plaincopy
     
    1. var reg = /(?=abc)www123/;  
    2. reg.exec("abc123"); // 结果 abc123  
    3. reg.exec("dbc123"); // 结果 null  

    我们结合这段代码来看,我们用了一个零宽断言(?=abc),它断言了在它出现的位置接下来的三个字符必须是abc,意即我断言接下来的www(当然,实际应用中不会写这么笨的代码,可以写成w{3},这里只是为了便于加深理解)三个字符必须为abc。所以这里,第一个结果能够匹配abc123,而第二个结果则显示匹配不到任何东西。零宽断言就是一种预言,它告诉表达式,接下来会发生什么。

    但是问题也来了:“零宽断言不是不匹配任何东西吗?那这里第一个结果怎么匹配了abc?”  对,没错,这个表达式是匹配到了abc,但是,并不是(?=abc)匹配的呀,而是后面的www匹配到的,我只是预测了www将会是abc不是吗?大笑

    应用场景

    说到这里,不少人会问了:我用 /abc123/ 不就可以匹配了吗?用你这个正则的意义何在?

    没错,这个正则把简单的问题复杂化了。但是存在的,就是有意义的。实际使用中,零宽断言(再次强调这里单指零宽度正预测先行断言)多用在匹配它之前的内容,而不是之后的内容。怎么理解呢?请看代码:

    [javascript] view plaincopy
     
    1. var reg = /abc(?=123)/;  
    2. reg.exec("abc123"); // 结果 abc  

    这里,我的零宽断言放在abc之后,也就是说,我断言abc之后必然要出现123。但是断言不是匹配——我只是预测将要出现的东西,我不匹配任何东西(这就是零宽度的意义)。所以,匹配结果为 abc。
    上面说的是匹配它之前的内容的情况,那么匹配它之后的内容的情况就完全没有意义了吗?No!请看下例:

    [javascript] view plaincopy
     
    1. /^(?=^.{8,}$)(?=.*d)(?=.*W+)(?=.*[A-Z])(?=.*[a-z])(?!.* ).*$/  

    这个看起来比较复杂,是一个js群里的人发的,一个十分巧妙的正则。不过,分解开来其实一点也不复杂,这是由五个零宽断言和一个负向零宽断言(负向表否定)组成。前五个分别是:必须是8位以上、必须出现数字、必须出现特殊符号(非字母数字下划数)、必须出现大写字母、必须出现小写字母。最后一个负向零宽断言是:不得出现换行。大家会发现每个断言都是以 .* 开头,那这个表示什么意思呢?因为这些断言都是写在同一位置,而同一位置是不可能同时出现以上六种情况的。所以用 .* 来告诉表达式,这个断言之前可以有字符(意即这个断言可以出现在接下来的字符串任何位置)。那么这条正则表达式有什么作用呢?有做过密码验证的同学马上就反应过来了,这可以用来做强密码验证。

    最后的一些唠叨

    现在再回过头来看看之前的思考题,是不是一下子就明白了问题所在?(?=a) 断言了它后面必须出现a,但是紧接着的表达式却给了一个b,很显然,这个表达式本身就是不成立的。

    那么,做一下修改 /(?=a)wb/ 不就成立了吗?可是,我之所以用零宽断言,就是想要匹配b呀,不想匹配a呀。那既然这个断言不能满足,我该怎么办呢?

    对于这种情况,那我们就不该用零宽度正预测先行断言(?=exp),而是该用零宽度正回顾后发断言(?<=exp)了。

    [javascript] view plaincopy
     
    1. var reg = /(?<=a)b/;  
    2. reg.exec("ab"); // 结果 b  

    不过很可惜,目前javascript是不支持这种断言的(ps.好像好多语言都不支持吧 ^-^),所以以上的代码只存在于理论中,无法在js环境中运行。

  • 相关阅读:
    Winform程序及dll打包成一个可执行的exe
    DotfuscatorPro防止反编译&ILSpy反编译
    C# 7-zip 压缩和解压缩
    ASP.NET MVC使用JWT代替session,实现单点登陆
    C#动态实体集的反序列化(动态JSON反序列化)
    FTP服务安装及使用
    未能加载文件或程序集“Microsoft.Web.Infrastructure, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35”或它的某一个依赖项。系统找不到指定的文件。
    项目在服务上运行出现中文水印乱码问题解决(第二篇)
    图片加水印信息处理及中文乱码解决
    MongoDB 索引
  • 原文地址:https://www.cnblogs.com/fcsh820/p/3479487.html
Copyright © 2020-2023  润新知