JavaScript正则表达式-零宽断言
介绍:
零宽断言的意思是(匹配宽度为零,满足一定的条件/断言)
零宽断言用于查找在某些内容(但并不包括这些内容)之前或之后的东西,也就是说它们像\b,^,$那样用于指定一个位置,这个位置应该满足一定的条件(即断言)。
分类:
零宽断言分为两类:
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)
例子:(具体解析过程看文章的解析过程部分)
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
例子:(具体解析过程看文章的解析过程部分)
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)
例子:(具体解析过程看文章的解析过程部分)
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
例子:(具体解析过程看文章的解析过程部分)
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的)
继续匹配到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)
例子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})+\b/ console.log(reg7.exec(str7)) // ["234567890", "890", index: 1, input: "1234567890", groups: undefined] var reg8 = new RegExp(/\b(?=re)\w+\b/) 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 = /\b\w+(?=ing\b)/ 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(/\b\w+(?<=ing\b)/) console.log(reg3.exec(str3)) // ["reading", index: 5, input: "I am reading", groups: undefined] var str4 = 'reading a book' var reg4 = /(?<=\bre)\w+\b/ 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]
欢迎补充!欢迎纠正!