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)

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})+\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]

 

欢迎补充!欢迎纠正!

posted @ 2020-10-29 11:57  hiuman  阅读(560)  评论(0编辑  收藏  举报