zoukankan      html  css  js  c++  java
  • JavaScript正则表达式-零宽断言

    介绍:

    零宽断言的意思是(匹配宽度为零,满足一定的条件/断言)

    零宽断言用于查找在某些内容(但并不包括这些内容)之前或之后的东西,也就是说它们像,^,$那样用于指定一个位置,这个位置应该满足一定的条件(即断言)。

    分类:

    零宽断言分为两类:

    1. 正向零宽断言

    2. 负向零宽断言

    零宽断言分为四种:

    1. 正向零宽先行断言:x(?=y),表示:x 被 y 跟随时匹配 x

    2. 正向零宽后发断言:(?<=y)x,表示:x 跟随 y 的情况下匹配 x

    3. 负向零宽先行断言:x(?!y),表示:x 没有被 y 紧随时匹配 x

    4. 负向零宽后发断言:(?<!y)x,表示:x 不跟随 y 时匹配 x

    关于名字:(不用太纠结)

    正向零宽先行断言的英文是Lookahead assertion,翻译可能不统一,有的会省略“正向零宽”,直接叫先行断言、向前断言

    正向零宽后发断言的英文是Lookbehind assertion,翻译可能不统一,有的会省略“正向零宽”,直接叫后发断言、向后断言、后行断言

    负向零宽先行断言的英文是Negative lookahead assertion,翻译可能不统一,有的叫向前否定断言

    负向零宽后发断言的英文是Negative lookbehind assertion,翻译可能不统一,有的叫向后否定断言

     

    详细讲解:(具体解析过程看文章的解析过程部分

    1. 正向零宽断言:

    1.1 先行断言(正向零宽先行断言)

    写法:x(?=y)

    x 被 y 跟随时匹配 x

    例子:具体解析过程看文章的解析过程部分

    var str1 = 'cooking'
    var str2 = 'cooking dance'
    
    var reg1 = new RegExp(/[a-z]*(?=ing)/)
    console.log(reg1.exec(str1)) // ["cook", index: 0, input: "cooking", groups: undefined]
    console.log(reg1.exec(str2)) // ["cook", index: 0, input: "cooking dance", groups: undefined]

    注意:

    先行断言的执行步骤大概是这样的:(具体解析过程看文章的解析过程部分)

    ①. 先从0开始匹配(字符串中的最左端

    ②. 找到满足[a-z]*的字符,然后再匹配(?=ing),若无法匹配。则更新索引位置(先缩减右侧索引,当右侧索引不能再缩减时,增加左侧索引)继续重复②步骤

    关于先行断言是从最左端开始匹配还是从最右端开始匹配的问题:

    var str1 = 'cooking singing going'
    
    var reg1 = new RegExp(/[a-z]*(?=ing)/)
    console.log(str1.match(reg1)) // ["cook", index: 0, input: "cooking singing going", groups: undefined]
    
    var reg2 = new RegExp(/[a-z]*(?=ing)/g)
    console.log(str1.match(reg2)) // ["cook", "", "sing", "", "go", ""]

    我在查相关资料的时候,发现很多博客都说先行断言是从最右端开始匹配,但是从例子中可以看出来,先行断言是从最左端开始匹配的。

    1.2 后发断言(正向零宽后发断言)

    写法:(?<=y)x

    x 跟随 y 的情况下匹配 x

    例子:具体解析过程看文章的解析过程部分

    var str1 = 'abcdefg'
    var str2 = 'abcdefgabc'
    
    var reg1 = new RegExp(/(?<=abc).*/)
    console.log(reg1.exec(str1)) // ["defg", index: 3, input: "abcdefg", groups: undefined]
    console.log(reg1.exec(str2)) // ["defgabc", index: 3, input: "abcdefgabc", groups: undefined]

    var reg2 = new RegExp(/.*(?<=abc)/) console.log(reg2.exec(str1)) // ["abc", index: 0, input: "abcdefg", groups: undefined] console.log(reg2.exec(str2)) // ["abcdefgabc", index: 0, input: "abcdefgabc", groups: undefined]

    注意:

    后发断言跟先行断言恰恰相反,它的执行步骤是这样的:具体解析过程看文章的解析过程部分

    ①. 先从0开始匹配(字符串中的最左端

    ②. 然后向左匹配(?<=abc),若无法匹配,则更新索引位置(索引加一)。当满足(?<=abc)匹配时,匹配满足第二个条件.*的字符。以此类推重复步骤②,找到完全满足的字符

    关于后发断言是从最左端开始匹配还是从最右端开始匹配的问题:

    var str1 = 'abcdefgabc'
    
    var reg1 = new RegExp(/(?<=abc).*/)
    console.log(str1.match(reg1)) // ["defgabc", index: 3, input: "abcdefgabc", groups: undefined]
    
    var reg2 = new RegExp(/(?<=abc).*/g)
    console.log(str1.match(reg2)) // ["defgabc", ""]

    从例子中可以看出来,后发断言是从最左端开始匹配的

    2. 负向零宽断言

    负向零宽断言也是匹配一个零宽度的位置,不过这个位置的“断言”取表达式的反值

    负向零宽断言要注意的跟正向的一样

    2.1 先行断言(负向零宽先行断言)

    写法:x(?!y)

    x 没有被 y 紧随时匹配 x

    例子:具体解析过程看文章的解析过程部分

    var str1 = 'abcdefg'
    var str2 = 'abcdefgabg'
    
    var reg1 = /ab(?!cd)/
    console.log(reg1.exec(str1)) // null
    console.log(reg1.exec(str2)) // ["ab", index: 7, input: "abcdefgabg", groups: undefined]

    2.2 后发断言(负向零宽后发断言)

    写法:(?<!y)x

    x 不跟随 y 时匹配 x

    例子:具体解析过程看文章的解析过程部分

    var str = '666-333'
    var reg1 = /(?<!-)d+/ console.log(reg1.exec(str)) // ["666", index: 0, input: "666-333", groups: undefined] var reg2 = /d+(?<!-)/ console.log(reg2.exec(str)) // ["666", index: 0, input: "666-333", groups: undefined]

    解析过程:

    使用工具调试后,发现:正则的位置匹配(方向)是从最左端开始(向右)匹配的,零宽断言的匹配(方向)跟先行断言和后发断言有关,先行断言向后匹配,后发断言向左匹配

    例子1. 使用:[a-z]*(?=ing)匹配“cooking”(看完以后自己联想“cooking dance”)

    从0开始匹配,满足[a-z]*条件的是一直到7的位置(cooking),不满足(?=ing)。(因为是零宽,所以是不包含ing的)

      继续匹配到6的位置(cookin),满足[a-z]*,不满足(?=ing)。

      继续匹配到5的位置(cooki),满足[a-z]*,不满足(?=ing)。

      继续匹配到4的位置(cook),满足[a-z]*,往右匹配i、n、g(一位一位检查),满足(?=ing),得到结果:ook。

    继续匹配。(因为已经匹配到4,所以从4开始)

    从4开始匹配,满足[a-z]*条件的是一直到7的位置(ing),不满足(?=ing)。

      继续匹配到6的位置(in),满足[a-z]*,不满足(?=ing)。

      继续匹配到5的位置(i),满足[a-z]*,不满足(?=ing)。

      继续匹配到4的位置(),满足[a-z]*,往右匹配i、n、g(一位一位检查),满足(?=ing),得到结果:(空串)。

    继续匹配。

    从5开始匹配,满足[a-z]*条件的是一直到7的位置(ng),不满足(?=ing)。

      继续匹配到6的位置(n),满足[a-z]*,不满足(?=ing)。

      继续匹配到5的位置(),满足[a-z]*,不满足(?=ing)。

    继续匹配。

    从6开始匹配,满足[a-z]*条件的是一直到7的位置(g),不满足(?=ing)。

      继续匹配到6的位置(),满足[a-z]*,不满足(?=ing)。

    例子2. 使用:(?<=abc).*匹配“abcdefg”(看完以后自己联想“abcdefgabc”)

    从0开始匹配,不满足(?<=abc)。

    继续匹配。

    从1开始匹配,不满足(?<=abc)。

    继续匹配。

    从2开始匹配,不满足(?<=abc)。

    继续匹配。

    从3开始匹配,往左匹配是否满足c、b、a(一位一位检查),满足(?<=abc)

      从3到7位置(defg),满足.*,得到结果:defg。
     

    例子2-2. 使用:.*(?<=abc)匹配“abcdefg”(看完以后自己联想“abcdefgabc”)

    从0开始匹配,到位置7(abcdefg),满足.*,不满足(?<=abc)

      继续匹配到6位置(abcdef),满足.*,不满足(?<=abc)

      继续匹配到5位置(abcde),满足.*,不满足(?<=abc)

      继续匹配到4位置(abcd),满足.*,不满足(?<=abc)

      继续匹配到3位置(abc),满足.*,向左匹配是否满足c、b、a(一位一位检查),满足(?<=abc),得到结果:abc

    继续匹配。(因为已经匹配到3,所以从3开始)

    从3开始匹配,到位置7(defg),满足.*,不满足(?<=abc)

      继续匹配到6位置(def),满足.*,不满足(?<=abc)

      继续匹配到5位置(de),满足.*,不满足(?<=abc)

      继续匹配到4位置(d),满足.*,不满足(?<=abc)

      继续匹配到3位置(),满足.*,向左匹配是否满足c、b、a(一位一位检查),满足(?<=abc),得到结果:(空串)

    继续匹配。

    从4开始匹配,到位置7(efg),满足.*,不满足(?<=abc)

      继续匹配到6位置(ef),满足.*,不满足(?<=abc)

      继续匹配到5位置(e),满足.*,不满足(?<=abc)

      继续匹配到4位置(),满足.*,不满足(?<=abc)

    继续匹配。

    从5开始匹配,到位置7(fg),满足.*,不满足(?<=abc)

      继续匹配到6位置(f),满足.*,不满足(?<=abc)

      继续匹配到5位置(),满足.*,不满足(?<=abc)

    继续匹配。

    从6开始匹配,到位置7(g),满足.*,不满足(?<=abc)

      继续匹配到6位置(),满足.*,不满足(?<=abc)

    例子3. 使用:ab(?!cd)匹配“abcdefg”(看完以后自己联想“abcdefgabg”)

    从0开始匹配,

      到1位置(a),满足ab。

      到2位置(ab),满足ab。

      到3位置(abc),满足(?!cd)。

      到4位置(abcd),不满足(?!cd)。

    继续匹配。

    从1开始匹配(b),不满足ab。

    继续匹配。

    从2开始匹配(c),不满足ab。

    继续匹配。

    从3开始匹配(d),不满足ab。

    继续匹配。

    从4开始匹配(e),不满足ab。

    继续匹配。

    从5开始匹配(f),不满足ab。

    继续匹配。

    从6开始匹配(g),不满足ab。

    例子4. 使用:(?<!-)d+匹配“666-333”(看完以后自己联想“666-333.2”)

    从0开始匹配,到位置3(666),左侧没有字符,满足(?<!-),满足d+,得到结果:666

    继续匹配。(因为已经匹配到3,所以从3开始)

    从3开始匹配(-),左侧是6,满足(?<!-),不满足d+

    继续匹配。

    从4开始匹配(3),左侧是-,不满足(?<!-)

    继续匹配。

    从5开始匹配(33),满足(?<!-),满足d+,得到结果:33

     

    例子4-2. 使用:d+(?<!-)匹配“666-333”(看完以后自己联想“666-333.2”)

    从0开始匹配,到位置3(666),满足d+,满足(?<!-),得到结果:666

    继续匹配。(因为已经匹配到3,所以从3开始)

    从3开始匹配(-),不满足d+

    继续匹配。

    从4开始匹配(333),满足d+,满足(?<!-),得到结果:333

    小测一下:

    var str1 = 'i am cooking meat.'
    var str2 = 'cooking singing going'
    
    var reg1 = new RegExp(/[a-z]*(?=ing)/)
    console.log(reg1.exec(str1)) // ["cook", index: 5, input: "i am cooking meat.", groups: undefined]
    console.log(reg1.exec(str2)) // ["cook", index: 0, input: "cooking singing going", groups: undefined]
    var str1 = 'abZW863ab88'
    var reg1 = /ab(?![A-Z])/g
    console.log(reg1.exec(str1)) // ["ab", index: 7, input: "abZW863ab88", groups: undefined]
    var str = '3'
    var reg = /(?<!-)d+/
    console.log(reg.exec(str)) // ["3", index: 0, input: "3", groups: undefined]
    var str5 = 'abZW863'
    var reg5 = /ab(?=[A-Z])/
    console.log(reg5.exec(str5)) // ["ab", index: 0, input: "abZW863", groups: undefined]
    
    var str6 = '<div>antzone'
    var reg6 = /^(?=<)<[^>]+>w+/
    console.log(reg6.exec(str6)) // ["<div>antzone", index: 0, input: "<div>antzone", groups: undefined]
    
    var str7 = '1234567890'
    var reg7 = /((?=d)d{3})+/
    console.log(reg7.exec(str7)) // ["234567890", "890", index: 1, input: "1234567890", groups: undefined]
    
    var reg8 = new RegExp(/(?=re)w+/)
    var str8 = 'reading a book'
    console.log(reg8.exec(str8)) // ["reading", index: 0, input: "reading a book", groups: undefined]
    
    var reg9 = new RegExp(/w+(?=ing)/)
    var str9 = 'muing'
    console.log(reg9.exec(str9)) // ["mu", index: 0, input: "muing", groups: undefined]
    
    var str10 = "I'm singing while you're dancing."
    var reg10 = /w+(?=ing)/
    console.log(reg10.exec(str10)) // ["sing", index: 4, input: "I'm singing while you're dancing.", groups: undefined]
    
    console.log('-------------')
    
    var str3 = 'I am reading'
    var reg3 = new RegExp(/w+(?<=ing)/)
    console.log(reg3.exec(str3)) // ["reading", index: 5, input: "I am reading", groups: undefined]
    
    var str4 = 'reading a book'
    var reg4 = /(?<=re)w+/
    console.log(reg4.exec(str4)) // ["ading", index: 2, input: "reading a book", groups: undefined]
    
    console.log('--------------')
    
    var str = 'welcome! hello world! hi!'
    var reg1 = /.*(?=hello)/
    var reg2 = /.*(?<=hello)/
    var reg3 = /.*(?!hello)/
    var reg4 = /.*(?<!hello)/
    console.log(reg1.exec(str)) // ["welcome! ", index: 0, input: "welcome! hello world! hi!", groups: undefined]
    console.log(reg2.exec(str)) // ["welcome! hello", index: 0, input: "welcome! hello world! hi!", groups: undefined]
    console.log(reg3.exec(str)) // ["welcome! hello world! hi!", index: 0, input: "welcome! hello world! hi!", groups: undefined]
    console.log(reg4.exec(str)) // ["welcome! hello world! hi!", index: 0, input: "welcome! hello world! hi!", groups: undefined]
    
    var reg5 = /(?=hello).*/
    var reg6 = /(?<=hello).*/
    var reg7 = /(?!hello).*/
    var reg8 = /(?<!hello).*/
    console.log(reg5.exec(str)) // ["hello world! hi!", index: 9, input: "welcome! hello world! hi!", groups: undefined]
    console.log(reg6.exec(str)) // [" world! hi!", index: 14, input: "welcome! hello world! hi!", groups: undefined]
    console.log(reg7.exec(str)) // ["welcome! hello world! hi!", index: 0, input: "welcome! hello world! hi!", groups: undefined]
    console.log(reg8.exec(str)) // ["welcome! hello world! hi!", index: 0, input: "welcome! hello world! hi!", groups: undefined]

    欢迎补充!欢迎纠正!

  • 相关阅读:
    Qt 串口通信 高速发送出错的解决方法总结
    Qt VS MFC
    从char到QChar
    QObject 的拷贝构造和赋值操作
    qt 元对象系统
    QT Embedded二三事之QObject的元对象
    QTableWidget与QTableView的区别
    arcengine,深入理解游标Cursors,实现数据的快速查找,插入,删除,更新
    利用IIdentify接口实现点选和矩形选择要素
    IWorkSpace与IWorkSpaceFactory与IWorkSpaceEdit
  • 原文地址:https://www.cnblogs.com/hiuman/p/13895953.html
Copyright © 2011-2022 走看看