正則表達式的先行斷言和後行斷言一共有4種形式:
(?=pattern) 零寬正向先行斷言(zero-width positive lookahead assertion)
(?!pattern) 零寬負向先行斷言(zero-width negative lookahead assertion)
(?<=pattern) 零寬正向後行斷言(zero-width positive lookbehind assertion)
(?<!pattern) 零寬負向後行斷言(zero-width negative lookbehind assertion)
這裏面的pattern是一個正則表達式。html
如同^表明開頭,$表明結尾,\b表明單詞邊界同樣,先行斷言和後行斷言也有相似的做用,它們只匹配某些位置,在匹配過程當中,不佔用字符,因此被稱爲「零寬」。所謂位置,是指字符串中(每行)第一個字符的左邊、最後一個字符的右邊以及相鄰字符的中間(假設文字方向是頭左尾右)。
下面分別舉例來講明這4種斷言的含義。前端
(?=pattern) 正向先行斷言
表明字符串中的一個位置,緊接該位置以後的字符序列可以匹配pattern。
例如對」a regular expression」這個字符串,要想匹配regular中的re,但不能匹配expression中的re,能夠用」re(?=gular)」,該表達式限定了re右邊的位置,這個位置以後是gular,但並不消耗gular這些字符,將表達式改成」re(?=gular).」,將會匹配reg,元字符.匹配了g,括號這一砣匹配了e和g之間的位置。正則表達式
(?!pattern) 負向先行斷言
表明字符串中的一個位置,緊接該位置以後的字符序列不能匹配pattern。
例如對」regex represents regular expression」這個字符串,要想匹配除regex和regular以外的re,能夠用」re(?!g)」,該表達式限定了re右邊的位置,這個位置後面不是字符g。負向和正向的區別,就在於該位置以後的字符可否匹配括號中的表達式。express
(?<=pattern) 正向後行斷言
表明字符串中的一個位置,緊接該位置以前的字符序列可以匹配pattern。
例如對」regex represents regular expression」這個字符串,有4個單詞,要想匹配單詞內部的re,但不匹配單詞開頭的re,能夠用」(?<=\w)re」,單詞內部的re,在re前面應該是一個單詞字符。之因此叫後行斷言,是由於正則表達式引擎在匹配字符串和表達式時,是從前向後逐個掃描字符串中的字符,並判斷是否與表達式符合,當在表達式中遇到該斷言時,正則表達式引擎須要往字符串前端檢測已掃描過的字符,相對於掃描方向是向後的。ide
(?<!pattern) 負向後行斷言
表明字符串中的一個位置,緊接該位置以前的字符序列不能匹配pattern。
例如對」regex represents regular expression」這個字符串,要想匹配單詞開頭的re,能夠用」(?<!\w)re」。單詞開頭的re,在本例中,也就是指不在單詞內部的re,即re前面不是單詞字符。固然也能夠用」\bre」來匹配。測試
對於這4個斷言的理解,能夠從兩個方面入手:
1.關於先行(lookahead)和後行(lookbehind):正則表達式引擎在執行字符串和表達式匹配時,會從頭至尾(從前到後)連續掃描字符串中的字符,設想有一個掃描指針指向字符邊界處並隨匹配過程移動。先行斷言,是當掃描指針位於某處時,引擎會嘗試匹配指針還未掃過的字符,先於指針到達該字符,故稱爲先行。後行斷言,引擎會嘗試匹配指針已掃過的字符,後於指針到達該字符,故稱爲後行。
2.關於正向(positive)和負向(negative):正向就表示匹配括號中的表達式,負向表示不匹配。this
對這4個斷言形式的記憶:
1.先行和後行:後行斷言(?<=pattern)、(?<!pattern)中,有個小於號,同時也是箭頭,對於自左至右的文本方向,這個箭頭是指向後的,這也比較符合咱們的習慣。把小於號去掉,就是先行斷言。
2.正向和負向:不等於(!=)、邏輯非(!)都是用!號來表示,因此有!號的形式表示不匹配、負向;將!號換成=號,就表示匹配、正向。3d
咱們常常用正則表達式來檢測一個字符串中包含某個子串,要表示一個字符串中不包含某個字符或某些字符也很容易,用[^...]形式就能夠了。要表示一個字符串中不包含某個子串(由字符序列構成)呢?
用[^...]這種形式就不行了,這時就要用到(負向)先行斷言或後行斷言、或同時使用。
例如判斷一句話中包含this,但不包含that。
包含this比較好辦,一句話中不包含that,能夠認爲這句話中每一個字符的前面都不是that或每一個字符的後面都不是that。正則表達式以下:
^((?<!that).)*this((?<!that).)*$ 或 ^(.(?!that))*this(.(?!that))*$
對於」this is the case」這句話,兩個表達式都可以匹配成功,而」note that this is the case」都匹配失敗。
在通常狀況下,這兩個表達式基本上都可以知足要求了。考慮極端狀況,如一句話以that開頭、以that結尾、that和this連在一塊兒時,上述表達式就可能不勝任了。
如」note thatthis is the case」或者」this is the case, not that」等。
只要靈活運用這幾個斷言,就很容易解決:
^(.(?<!that))*this(.(?<!that))*$
^(.(?<!that))*this((?!that).)*$
^((?!that).)*this(.(?<!that))*$
^((?!that).)*this((?!that).)*$
這4個正則表達式測試上述的幾句話,結果都可以知足要求。指針
上述4種斷言,括號裏的pattern自己是一個正則表達式。但對2種後行斷言有所限制,在Perl和Python中,這個表達式必須是定長(fixed length)的,即不能使用*、+、?等元字符,如(?<=abc)沒有問題,但(?<=a*bc)是不被支持的,特別是當表達式中含有|鏈接的分支時,各個分支的長度必須相同。之因此不支持變長表達式,是由於當引擎檢查後行斷言時,沒法肯定要回溯多少步。Java支持?、{m}、{n,m}等符號,但一樣不支持*、+字符。Javascript乾脆不支持後行斷言,不過通常來講,這不是太大的問題。參見這裏。htm
試驗例子
sole sorry chilly high tight laughter
匹配h
匹配後面是t的h
匹配後面不是t的h
匹配前面是g的h
匹配前面不是g的h
先行斷言和後行斷言某種程度上就比如使用if語句對匹配的字符先後作判斷驗證。