本網站的更多內容 |
簡介 |
正規表示式快速入門 |
正規表示式教學 |
替換字串教學 |
應用程式和語言 |
正規表示式範例 |
正規表示式參考 |
替換字串參考 |
書籍評論 |
可列印 PDF |
關於本網站 |
RSS Feed 和部落格 |
環顧,在 前一個主題 中有詳細介紹,是一個非常強大的概念。很不幸地,初學正規表示式的人常常沒有善用它,因為環顧有點令人困惑。令人困惑的地方在於環顧是零長度。因此,如果你有一個 regex,其中前瞻後面接著另一段 regex,或後顧前面接著另一段 regex,那麼 regex 會兩次掃描字串的一部分。
一個更實際的範例可以讓這點更清楚。假設我們想要找一個長度為六個字母且包含三個連續字母 cat 的字。實際上,我們可以在不使用環顧的情況下找到它。我們只要指定所有選項並使用 交替 將它們組合在一起:cat\w{3}|\wcat\w{2}|\w{2}cat\w|\w{3}cat。很簡單吧。但是如果你想要在長度介於 6 到 12 個字母之間的字中,找出包含「cat」、「dog」或「mouse」的字,這種方法就會變得難以使用。
在這個範例中,我們基本上有兩個成功配對的要求。首先,我們想要一個長度為 6 個字母的字。其次,我們找到的字必須包含「cat」這個字。
使用 \b\w{6}\b 就可以輕鬆配對一個 6 個字母的字。配對一個包含「cat」的字也一樣容易:\b\w*cat\w*\b。
將這兩者結合起來,我們得到:(?=\b\w{6}\b)\b\w*cat\w*\b。很簡單!以下是它的運作方式。在字串中嘗試正規表示式的每個字元位置時,引擎會先嘗試正向先視中的正規表示式。這個子正規表示式,因此先視,僅在字串中當前字元位置位於字串中 6 個字母字的開頭時才會配對。如果不是,先視會失敗,而引擎會從字串中下一個字元位置的開頭繼續嘗試正規表示式。
先視的長度為零。因此,當先視中的正規表示式找到 6 個字母的字時,字串中的當前位置仍然位於 6 個字母字的開頭。正規表示式引擎會在此位置嘗試正規表示式的其餘部分。由於我們已經知道可以在當前位置配對 6 個字母的字,因此我們知道 \b 會配對,而第一個 \w* 會配對 6 次。然後,引擎會 回溯,減少 \w* 配對的字元數,直到可以配對 cat。如果無法配對 cat,引擎別無選擇,只能從正規表示式的開頭重新開始,在字串中的下一個字元位置。這位於我們剛剛找到的 6 個字母字的第二個字母,先視會在此失敗,導致引擎逐字元前進,直到下一個 6 個字母字。
如果 cat 可以成功配對,第二個 \w* 會消耗 6 個字母單字中剩下的字母(如果有)。之後,正規表示式中的最後一個 \b 保證會配對到第二個 lookahead 中的 \b 所配對的位置。我們的雙重需求正規表示式已成功配對。
雖然以上的正規表示式運作良好,但它並非最佳的解決方案。如果你只是在文字編輯器中進行搜尋,這並不成問題。但是,如果你要將這個正規表示式重複使用,或者在開發的應用程式中使用在大量的資料上,最佳化是很好的做法。
如果你仔細檢查正規表示式,並追蹤正規表示式引擎如何套用它,就像我們在上面所做的一樣,你可以自己找出這些最佳化。第三個且最後一個 \b 保證會配對。由於 字詞邊界 是零長度,因此不會改變正規表示式引擎所回傳的結果,我們可以將它們移除,留下:(?=\b\w{6}\b)\w*cat\w*。雖然最後一個 \w* 也保證會配對,但我們無法將它移除,因為它會將字元加入正規表示式配對中。請記住,lookahead 會捨棄它的配對,因此它不會影響正規表示式引擎所回傳的配對。如果我們省略 \w*,產生的配對將會是包含「cat」的 6 個字母單字的開頭,直到「cat」包含在內,而不是整個單字。
但是,我們可以最佳化第一個 \w*。就目前的情況,它將會配對 6 個字母,然後回溯。但是,我們知道在成功的配對中,「cat」之前最多只有 3 個字母。因此,我們可以最佳化為 \w{0,3}。請注意,將星號設為惰性並不足以最佳化。惰性星號會較快找到成功的配對,但是如果 6 個字母的單字不包含「cat」,它仍然會導致正規表示式引擎嘗試在最後兩個字母、最後一個字母,甚至在 6 個字母單字之後的一個字元中配對「cat」。
因此,我們有 (?=\b\w{6}\b)\w{0,3}cat\w*。最後一個微小的最佳化涉及第一個 \b。由於它本身是零長度,因此不需要將它放在 lookahead 中。因此,最後的正規表示式為:\b(?=\w{6}\b)\w{0,3}cat\w*。
您也可以將最後的 \w* 替換為 \w{0,3}。但這不會有任何差別。先行斷言已經檢查我們是否位於 6 個字母的字詞,而 \w{0,3}cat 已經比對了該字詞的 3 到 6 個字母。我們是否以 \w* 或 \w{0,3} 結束正規表示式並不重要,因為無論如何,我們都會比對所有剩下的字詞字元。由於產生的比對結果和找到它的速度相同,我們不妨使用較容易輸入的版本。
那麼,您會使用什麼來找出任何介於 6 到 12 個字母長,且包含「cat」、「dog」或「mouse」的字詞?我們再次有兩個需求,我們可以使用先行斷言輕鬆地將它們合併:\b(?=\w{6,12}\b)\w{0,9}(cat|dog|mouse)\w*。一旦您掌握訣竅,這非常容易。這個正規表示式也會將「cat」、「dog」或「mouse」放入第一個反向參照。
| 快速入門 | 教學 | 工具和語言 | 範例 | 參考 | 書籍評論 |
| 簡介 | 目錄 | 特殊字元 | 非列印字元 | 正規表示式引擎內部 | 字元類別 | 字元類別減法 | 字元類別交集 | 簡寫字元類別 | 點 | 錨點 | 字詞邊界 | 交替 | 選用項目 | 重複 | 群組和擷取 | 反向參照 | 反向參照,第 2 部分 | 命名群組 | 相對反向參照 | 分支重設群組 | 自由間距和註解 | Unicode | 模式修改器 | 原子群組 | 佔有量詞 | 先行斷言和後行斷言 | 先行斷言和後行斷言,第 2 部分 | 將文字保留在比對結果之外 | 條件式 | 平衡群組 | 遞迴 | 子常式 | 無限遞迴 | 遞迴和量詞 | 遞迴和擷取 | 遞迴和反向參照 | 遞迴和回溯 | POSIX 方括號表示式 | 零長度比對 | 繼續比對 |
網頁網址:https://regular-expressions.dev.org.tw/lookaround2.html
網頁最後更新:2024 年 2 月 7 日
網站最後更新:2024 年 3 月 15 日
版權所有 © 2003-2024 Jan Goyvaerts。保留所有權利。