《正則表達式經典實例(第2版)》——2.8 匹配多個選擇分支之一
本節書摘來自異步社區《正則表達式經典實例(第2版)》一書中的第2章,第2.8節,作者: 【美】Jan Goyvaerts , Steven Levithan著,更多章節內容可以訪問雲棲社區“異步社區”公眾號查看
2.8 匹配多個選擇分支之一
問題描述
創建一個正則表達式,當把它重複應用到目標文本Mary, Jane, and Sue went to Mary's house之上時,會匹配到Mary、Jane、Sue,且能再次匹配到Mary。之後再進行的匹配嚐試都會失敗。
解決方案
Mary|Jane|Sue
正則選項:無
正則流派:.NET、Java、JavaScript、PCRE、Perl、Python、Ruby
討論
豎線(vertical bar),或稱作管道符號(pipe symbol),會把正則表達式拆分成多個選擇分支。‹Mary|Jane|Sue›會在每次匹配嚐試時匹配Mary,或者Jane,或者Sue。每次隻會匹配一個名字,但是每次卻可以匹配不同的名字。
本書中的所有正則表達式流派都會使用正則製導的引擎。正則表達式依賴於這台引擎來工作。這裏所說的正則製導1FF的含義是,在目標文本中的每個字符位置會首先匹配該正則表達式的所有可能排列,然後才會到下一個字符位置進行匹配嚐試。
當你把‹Mary|Jane|Sue›應用到Mary, Jane, and Sue went to Mary's house的時候,在字符串起始處立即就會找到匹配Mary。
當你把同一個正則表達式應用到餘下的字符串的時候,比如說,你可以在文本編輯器中單擊“查找下一個”,正則引擎就會嚐試在該字符串中的第一個逗號處匹配‹Mary›。匹配會失敗。然後,它會在同一個位置嚐試去匹配‹Jane›,這也會失敗。接著在逗號處匹配‹Sue›,當然也會失敗。隻有在所有匹配都失敗之後,正則引擎才會前進到字符串中的下一個字符。從第一個空格開始匹配,3個選擇分支都會得到同樣的失敗結果。
從字母J開始匹配的時候,第一個選擇分支‹Mary›會出現匹配失敗。接著第二個選擇分支,也就是‹Jane›,會在字母J處匹配成功。它匹配的是Jane。正則引擎宣布匹配成功。
需要注意的是:雖然在目標文本中還存在另外一個Mary,而且在正則表達式中‹Mary›出現在‹Jane›之前,但是這裏匹配到的依然是Jane。至少在這個例子中,正則表達式中各分支的順序並不重要。正則表達式會查找最左邊的匹配。它會從左向右掃描目標文本,在掃描的每一步中都會嚐試正則表達式中的所有選擇分支,而當其中任意一個選擇分支產生一個合法匹配的時候,匹配過程就會停在這個位置。
如果我們再次對字符串餘下部分進行查找,那麼會找到Sue。而第四次查找則會再一次找到Mary。如果你告訴正則引擎進行第五次查找,就會失敗,因為這三個選擇分支都無法匹配餘下的’s house字符串。
隻有當在字符串中的同一個位置存在兩個選擇分支同時匹配的時候,正則式中的選擇分支的順序才有意義。例如,正則式‹Jane|Janet›在匹配目標文本Her name is Janet的時候,就會有兩個選擇分支在同一位置出現匹配。在此正則表達式中並不存在單詞邊界。事實上,‹Jane›是否隻匹配到Her name is Janet中的單詞Janet的一部分並不重要。
‹Jane|Janet›之所以會匹配到Her name is Janet中的Jane,是因為一個正則製導的正則表達式引擎是遵循“淺嚐輒止”的工作原則的(eager)。除了會從左向右掃描目標文本,查找最左匹配之外,它還會從左向右掃描正則式中的選擇分支。而一旦它找到一個匹配的選擇分支,正則引擎就會立即停止。
當‹Jane|Janet›到達了Her name is Janet中的J的時候,第一個選擇分支‹Jane›,成功匹配。第二個選擇分支則根本沒有進行嚐試。如果我們告訴引擎接著查找下一個匹配的話,這時候在目標文本中剩下的隻有t。此時兩個選擇分支都不能成功匹配。
要想不讓Jane搶奪Janet的光環,有兩種方式。第一種方式是把較長的選擇分支放在前麵:‹Janet|Jane›。另外一種更為可靠的方式是清晰地表達我們所期望的結果:我們在查找名字,而名字應該是完整的單詞。正則表達式並不會處理單詞,但是它們可以處理單詞邊界。
因此,‹\bJane\b|\bJanet\b›和‹\bJanet\b|\bJane\b›都會匹配到Her name is Janet中的Janet。由於單詞邊界的原因,隻有一個選擇分支會成功匹配。在這裏我們看到選擇分支的順序依然無關緊要。
最後更新:2017-06-02 19:35:39