1. 程式人生 > 實用技巧 >淺析正則表示式用法:零寬斷言(?=,?<=,?!,?<!)

淺析正則表示式用法:零寬斷言(?=,?<=,?!,?<!)

  在使用正則表示式時,有時我們需要捕獲的內容前後必須是特定內容,但又不捕獲這些特定內容的時候,零寬斷言就起到作用了。

  正則表示式零寬斷言是正則表示式中的難點,所以重點從匹配原理方面進行分析。零寬斷言還有其他的名稱,例如"環視"或者"預搜尋"等等,不過這些都不是我們關注的重點。

一、基本概念

  零寬斷言正如它的名字一樣,是一種零寬度的匹配,它匹配到的內容不會儲存到匹配結果中去,最終匹配結果只是一個位置而已。

  作用是:給指定位置新增一個限定條件,用來規定此位置之前或者之後的字元必須滿足限定條件才能使正則中的字表達式匹配成功。

  注意:這裡所說的子表示式並非只有用小括號括起來的表示式,而是正則表示式中的任意匹配單元。

  javascript只支援零寬先行斷言,而零寬先行斷言又可以分為正向零寬先行斷言,和負向零寬先行斷言。

var reg = /ab(?=[A-Z])/;
"abZW863".match(reg)
// ["ab", index: 0, input: "abZW863", groups: undefined]

  在以上程式碼中,正則表示式的語義是:匹配後面跟隨任意一個大寫字母的字串"ab"。最終匹配結果是"ab",因為零寬斷言"(?=[A-Z])"並不匹配任何字元,只是用來規定當前位置的後面必須是一個大寫字母。

var reg = /ab(?![A-Z])/;
"abZW863".match(reg)
// null

  以上程式碼中,正則表示式的語義是:匹配後面不跟隨任意一個大寫字母的字串"ab"。正則表示式沒能匹配任何字元,因為在字串中,ab的後面跟隨有大寫字母。

二、匹配原理

  上面程式碼只是用概念的方式介紹了零寬斷言是如何匹配的,下面就以匹配原理的方式分別介紹一下正向零寬斷言和負向零寬斷言是如何匹配的。

1、正向零寬斷言

  程式碼例項如下:

var str="<div>antzone";
var reg=/^(?=<)<[^>]+>\w+/;
console.log(str.match(reg));

  匹配過程如下:首先由正則表示式中的""獲取控制權,首先由位置0開始進行匹配,它匹配開始位置0,匹配成功,然後控制權轉交給"(?=<)",由於"

"是零寬的,所以"(?=<)"也是從位置0處開始匹配,它要求所在的位置右側必須是字元"<",位置0的右側恰好是字元"<",匹配成功,然後控制權轉交個"<",由於"(?=<)"也是零寬的,所以它也是從位置0處開始匹配,於是匹配成功,後面的匹配過程就不介紹了。

2、負向零寬斷言

var reg = /ab(?![A-Z])/g;
"abZW863ab88".match(reg)
// ["ab"]

  匹配過程如下:首先由正則表示式的字元"a"獲取控制權,從位置0處開始匹配,匹配字元"a"成功,然後控制權轉交給"b",從位置1處開始匹配,配字元"b"成功,然後控制權轉交給"(?[A-Z])",它從位置2處開始匹配,它要求所在位置的右邊不能夠是任意一個大寫字母,而位置的右邊是大寫字母"Z",匹配失敗,然後控制權又重新交給字元"a",並從位置1處開始嘗試,匹配失敗,然後控制權再次交給字元"a",從位置2處開始嘗試匹配,依然失敗,如此往復嘗試,直到從位置7處開始嘗試匹配成功,然後將控制權轉交給"b",然後從位置8處開始嘗試匹配,匹配成功,然後再將控制權轉交給"(?[A-Z])",它從位置9處開始嘗試匹配,它規定它所在的位置右邊不能夠是大寫字母,匹配成功,但是它並不會真正匹配ab後面的字元,所以最終匹配結果是"ab"。

三、補充知識

  零寬斷言是正則表示式中的一種方法,正則表示式在電腦科學中,是指一個用來描述或者匹配一系列符合某個句法規則的字串的單個字串。

  正則表示式在電腦科學中,是指一個用來描述或者匹配一系列符合某個句法規則的字串的單個字串。在很多文字編輯器或其他工具裡,正則表示式通常被用來檢索和/或替換那些符合某個模式的文字內容。許多程式設計語言都支援利用正則表示式進行字串操作。例如,在Perl中就內建了一個功能強大的正則表示式引擎。正則表示式這個概念最初是由Unix中的工具軟體(例如sed和grep)普及開的。正則表示式通常縮寫成“regex”,單數有regexp、regex,複數有regexps、regexes、regexen。

  零寬斷言:用於查詢在某些內容(但並不包括這些內容)之前或之後的東西,也就是說它們像\b,^,$那樣用於指定一個位置,這個位置應該滿足一定的條件(即斷言),因此它們也被稱為零寬斷言。

  最好還是拿例子來說明吧: 斷言用來宣告一個應該為真的事實。正則表示式中只有當斷言為真時才會繼續進行匹配。

  最精簡有用的,看例子直接上手用:

1、(?=exp):零寬度正預測先行斷言,它斷言自身出現的位置的後面能匹配表示式exp。

  比如\b(?=re)\w+\b,匹配以re開頭的單詞,如查詢reading a book.時,它會匹配reading。

var reg = /\b(?=re)\w+\b/;
"reading a book".match(reg)
// ["reading", index: 0, input: "reading a book", groups: undefined]
var reg = /\w+(?=ing)/;
"muing".match(reg)
// ["mu", index: 0, input: "muing", groups: undefined]
匹配ing前面的位置
var reg = /\w+(?=_path)/;
"product_path".match(reg)
// ["product", index: 0, input: "product_path", groups: undefined]
// 匹配後面為_path,結果為product

2、(?!exp):零寬度負預測先行斷言,斷言此位置的後面不能匹配表示式exp

var reg = /(product)(?!_path)/;
"product_path".match(reg)
// null   匹配後面不是_path,未匹配

var reg = /(product)(?!_url)/;
"product_path".match(reg)
// (2)["product", "product", index: 0, input: "product_path", groups: undefined]
// 匹配後面不是_url,匹配上

3、(?<=exp):零寬度正回顧後發斷言,它斷言自身出現的位置的前面能匹配表示式exp

var reg = /(?<=name:)gwf/;
"name:gwf".match(reg)
// ["gwf", index: 5, input: "name:gwf", groups: undefined]

  匹配前面為 name:,匹配上,結果為 gwf

4、(?<!exp):零寬度負回顧後發斷言來斷言此位置的前面不能匹配表示式exp

var reg = /(?<!name:)gwf/;
"name:gwf".match(reg)
// null

var reg = /(?<!nick_name:)gwf/;
"name:gwf".match(reg)
// ["gwf", index: 5, input: "name:gwf", groups: undefined]

  匹配前面不是 name:,未匹配;

  匹配前面不是 nick_name:,匹配上,結果為 gwf

5、總結

  放一張總結圖,可以看