正規表示式案例分析 (二)

琅琊丶發表於2019-02-23

前言

最近在重新學習正規表示式,把在學習過程中所遇到的案例,還有比較難理解概念用自己的理解分析並整理總結。

如有哪些地方不對,歡迎指正,謝謝!(๑•ᴗ•๑)

本系列使用的JavaScript所支援的正規表示式語法,並推薦你使用 regexr.com/ 去做相應的練習。

假定你已經熟悉元字元方括號修飾符,量詞RegExp物件

如果沒有,請先看一遍文件
www.w3school.com.cn/jsref/jsref…

本系列同步GitHub github.com/Janking/Blo…

系列目錄

正規表示式案例分析 (一)

案例分析

(1) 重複分組

在上一篇介紹千分位的時候,就有用到重複分組,這是一個很容易理解錯誤的點,至少我開始的時候是理解錯誤的。

https://user-gold-cdn.xitu.io/2017/7/23/0752787423b4fe0c5aaa52f2126294ba
https://user-gold-cdn.xitu.io/2017/7/23/0752787423b4fe0c5aaa52f2126294ba

假定用這個正則去匹配 1234567890,開始我以為分組小括號(\d\d\d)的最終結果是 123456789都能拿到,但結果卻只有789

重複分組的匹配在每次引擎退出該分組的時候被捕獲,並會覆蓋該分組在之前匹配的任何文字

模擬一下引擎工作的步驟:

1. 第一次匹配,捕獲到 `123`,退出分組
2. 第二次匹配,捕獲到 `456`, 覆蓋上一次捕獲的`123`,退出分組
3. 第三次匹配,捕獲到 `789`,覆蓋上一次捕獲的`456`,退出分組
4. 退出重複分組,結束複製程式碼

因為重複分組最後一次迴圈儲存的是789,另外兩次分組匹配,也就是123,456是無法被獲取的。

如果想要獲得所有結果,就要把重複匹配放進分組中 /((\d\d\d){3})/

(2) 獲取<p>...</p>中的內容

https://user-gold-cdn.xitu.io/2017/7/23/92b63477e6366c36f7dac825f3bda438
https://user-gold-cdn.xitu.io/2017/7/23/92b63477e6366c36f7dac825f3bda438

/<p>(.*?)<\/p>/g.exec('<p>Hello,<em>world</em></p><p>Hello,Janking</p>')

執行結果
[
    "<p>Hello,<em>world</em></p>",
     "Hello,<em>world</em>", 
     index: 0, 
     input: "<p>Hello,<em>world</em></p><p>Hello,Janking</p>"
]複製程式碼

假如沒有?,而匹配結果就會差很遠,不信你看!

/<p>(.*)<\/p>/g.exec('<p>Hello,<em>world</em></p><p>Hello,Janking</p>')

執行結果
[
    "<p>Hello,<em>world</em></p><p>Hello,Janking</p>", 
    "Hello,<em>world</em></p><p>Hello,Janking", 
    index: 0, 
    input: "<p>Hello,<em>world</em></p><p>Hello,Janking</p>"
]複製程式碼

缺少了問號?,結果把中間的</p><p>也匹配進來了,為什麼一個字元不同,匹配結果就差異那麼大?這裡涉及到正規表示式中比較重要的概念:貪婪匹配,懶惰匹配,回溯

貪婪匹配

屬於貪婪模式的量詞,也叫做匹配優先量詞,包括:{m,n}{m,}?*+

惰性匹配

在匹配優先量詞後加上?,即變成屬於惰性匹配的量詞,也叫做忽略優先量詞,包括:{m,n}?{m,}???*?+?

回溯

當前前面分支/重複匹配成功後,沒有多餘的文字可被正則後半部分匹配時,會產生回溯

用一個簡單的例子來解釋一下貪婪匹配和惰性匹配!

貪婪 : /\d+\b/

惰性 : /d+?\b/

文字 : 1234a

貪婪正則匹配 1234a 時的過程是這樣的:

    1. \d+ 匹配得到 1234
    2. \b  卻匹配失敗,因為它的左邊必須是數字,而不是字母 (\b 是分詞邊界匹配,用來獲取位置,而不是文字,上一節有講到)
    4. 這個時候,\d+會嘗試回吐一個字元,即匹配結果為 123 ,可\b還是匹配失敗!
    5. 那就繼續回吐,一直到 1,還是匹配失敗,那麼這個正則就整體匹配失敗了
    6. 這個回吐匹配結果的過程就是回溯複製程式碼

惰性正則匹配 1234a 時的過程是這樣的:

    1. \d+? 首先匹配,結果是 1 ,緊接著 \b 匹配失敗
    2. 那就 \d+? 繼續匹配,結果是 12 ,緊接著 \b 還是匹配失敗
    3. \d+? 一直匹配到1234,緊接著的 \b 依然匹配失敗
    4. 結果整個正則匹配不成功複製程式碼

通過這兩個例子的比較,相信你會猜到回溯會影響匹配速度,回溯的過程慢那是相對那些DFA引擎。

而JS的正則引擎是NFA(非確定型有限自動機),匹配慢,編譯快。

結束語

本章節將的案例只有兩個,主要是涉及到幾個重要概念,佔了一定的篇幅,由於本人不喜歡把文章寫的太長,影響閱讀體驗,所以就此打住,從下一篇開始會重點的去分析常用正規表示式的匹配過程,敬請期待。

參考文獻:

  • 《精通正規表示式》
  • 《正規表示式經典例項》

相關文章