前言
最近在重新學習正規表示式,把在學習過程中所遇到的案例,還有比較難理解概念用自己的理解分析並整理總結。
如有哪些地方不對,歡迎指正,謝謝!(๑•ᴗ•๑)
本系列使用的JavaScript
所支援的正規表示式語法,並推薦你使用 regexr.com/ 去做相應的練習。
假定你已經熟悉元字元
,方括號
,修飾符
,量詞
及RegExp物件
。
如果沒有,請先看一遍文件
www.w3school.com.cn/jsref/jsref…
本系列同步GitHub github.com/Janking/Blo…
系列目錄
案例分析
(1) 重複分組
在上一篇介紹千分位的時候,就有用到重複分組,這是一個很容易理解錯誤的點,至少我開始的時候是理解錯誤的。
假定用這個正則去匹配 1234567890
,開始我以為分組小括號(\d\d\d)
的最終結果是 123
,456
,789
都能拿到,但結果卻只有789
重複分組的匹配在每次引擎退出該分組的時候被捕獲,並會覆蓋該分組在之前匹配的任何文字
模擬一下引擎工作的步驟:
1. 第一次匹配,捕獲到 `123`,退出分組
2. 第二次匹配,捕獲到 `456`, 覆蓋上一次捕獲的`123`,退出分組
3. 第三次匹配,捕獲到 `789`,覆蓋上一次捕獲的`456`,退出分組
4. 退出重複分組,結束複製程式碼
因為重複分組最後一次迴圈儲存的是789,另外兩次分組匹配,也就是123,456是無法被獲取的。
如果想要獲得所有結果,就要把重複匹配放進分組中 /((\d\d\d){3})/
(2) 獲取<p>...</p>
中的內容
/<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(非確定型有限自動機),匹配慢,編譯快。
結束語
本章節將的案例只有兩個,主要是涉及到幾個重要概念,佔了一定的篇幅,由於本人不喜歡把文章寫的太長,影響閱讀體驗,所以就此打住,從下一篇開始會重點的去分析常用正規表示式的匹配過程,敬請期待。
參考文獻:
- 《精通正規表示式》
- 《正規表示式經典例項》