1. 程式人生 > >先行斷言和後行斷言

先行斷言和後行斷言

script 字符串 pre ring ctu lac them 引擎 have

後行斷言

JavaScript 語言的正則表達式,只支持先行斷言(lookahead)和先行否定斷言(negative lookahead),不支持後行斷言(lookbehind)和後行否定斷言(negative lookbehind)。目前,有一個提案,引入後行斷言,V8 引擎 4.9 版已經支持。

”先行斷言“指的是,x只有在y前面才匹配,必須寫成/x(?=y)/。比如,只匹配百分號之前的數字,要寫成/\d+(?=%)/。”先行否定斷言“指的是,x只有不在y前面才匹配,必須寫成/x(?!y)/。比如,只匹配不在百分號之前的數字,要寫成/\d+(?!%)/

/\d+(?=%)/.exec(‘100% of US presidents have been male‘)  // ["100"]
/\d+(?!%)/.exec(‘that’s all 44 of them‘)                 // ["44"]

上面兩個字符串,如果互換正則表達式,就不會得到相同結果。另外,還可以看到,”先行斷言“括號之中的部分((?=%)),是不計入返回結果的。

“後行斷言”正好與“先行斷言”相反,x只有在y後面才匹配,必須寫成/(?<=y)x/。比如,只匹配美元符號之後的數字,要寫成/(?<=\$)\d+/。”後行否定斷言“則與”先行否定斷言“相反,x只有不在y後面才匹配,必須寫成/(?<!y)x/。比如,只匹配不在美元符號後面的數字,要寫成/(?<!\$)\d+/

/(?<=\$)\d+/.exec(‘Benjamin Franklin is on the $100 bill‘)  // ["100"]
/(?<!\$)\d+/.exec(‘it’s is worth about €90‘)                // ["90"]

上面的例子中,“後行斷言”的括號之中的部分((?<=\$)),也是不計入返回結果。

下面的例子是使用後行斷言進行字符串替換。

const RE_DOLLAR_PREFIX = /(?<=\$)foo/g;
‘$foo %foo foo‘.replace(RE_DOLLAR_PREFIX, ‘bar‘);
// ‘$bar %foo foo‘

上面代碼中,只有在美元符號後面的foo才會被替換。

“後行斷言”的實現,需要先匹配/(?<=y)x/x,然後再回到左邊,匹配y的部分。這種“先右後左”的執行順序,與所有其他正則操作相反,導致了一些不符合預期的行為。

首先,”後行斷言“的組匹配,與正常情況下結果是不一樣的。

/(?<=(\d+)(\d+))$/.exec(‘1053‘) // ["", "1", "053"]
/^(\d+)(\d+)$/.exec(‘1053‘) // ["1053", "105", "3"]

上面代碼中,需要捕捉兩個組匹配。沒有"後行斷言"時,第一個括號是貪婪模式,第二個括號只能捕獲一個字符,所以結果是1053。而"後行斷言"時,由於執行順序是從右到左,第二個括號是貪婪模式,第一個括號只能捕獲一個字符,所以結果是1053

其次,"後行斷言"的反斜杠引用,也與通常的順序相反,必須放在對應的那個括號之前。

/(?<=(o)d\1)r/.exec(‘hodor‘)  // null
/(?<=\1d(o))r/.exec(‘hodor‘)  // ["r", "o"]

上面代碼中,如果後行斷言的反斜杠引用(\1)放在括號的後面,就不會得到匹配結果,必須放在前面才可以。因為後行斷言是先從左到右掃描,發現匹配以後再回過頭,從右到左完成反斜杠引用。

先行斷言和後行斷言