讀《精通正則表達式》-- 網上 js 正則基礎教程沒有涉及的一些知識

正則起源

最近看完了 《精通正則表達式》,收穫頗豐,略過了一些晦澀難懂的理論部分,主要看了實戰和教程部分。javascript

下面引用一下百度百科裏的內容。java

正則表達式的「鼻祖」或許可一直追溯到科學家對人類神經系統工做原理的早期研究。美國新澤西州的Warren McCulloch和出生在美國底特律的Walter Pitts這兩位神經生理方面的科學家,研究出了一種用數學方式來描述神經網絡的新方法,他們創造性地將神經系統中的神經元描述成了小而簡單的自動控制元,從而做出了一項偉大的工做革新。

那麼寫正則是否是就是把本身神經工做過程經過正則表現出來呢? 好比讓小孩子在一堆圖形中找到匹配的圖形放入凹槽。mysql

正則引擎

正則分幾種引擎也從是本書得到的知識點之一。正則表達式

  • DFA
  • 傳統型NFA
  • POSIX NFA

NFA範圍更廣,例如 JAVA, PHP, Ruby, .NET... 你是看不起我javascript因此纔不列入的嗎?sql

使用DFA的是flex, MySQL, lex, awk大部分版本… 實話說,除了mysql,都沒聽過。不過不用在乎!數組

兩個引擎的區別。緩存

  • NFA 更注重表達式
  • DFA 文本主導

經過書中裏例子說,NFA 用表達式來匹配文本,而 DFA 是文原本匹配文表達式。當寫好一個正則以後,NFA 是先檢查表達式,同時檢查文本是否匹配這個表達式。而 DFA 則是先掃描文本,而後處理表達式中的全部匹配可能,若是匹配失敗,就將這條可能的線,淘汰。因此這裏衍生一個概念就是回溯,NFA 有回溯,而 DFA 沒有。網絡

知識點

做爲一個菜鳥,正則表達式一直是書到用時方恨少的角色。平時都是能抄則抄,不能抄的時候乾着急,只能用 substr, indexOf, chatAt等等的方法實現功能,既不優雅也不夠裝逼。上網學習也都是菜鳥教程,W3school。而後下面說一下以上兩個基礎教程裏沒說到的知識點。性能

括號捕獲與反向引用

當你在正則表達式裏使用了 (),在表達式匹配時,它能記住或者說緩存括號內匹配的結果,從而能夠拿到括號內的結果,能夠重複使用或者只須要括號內的結果,來剔除不須要的匹配內容。學習

// 咱們常常會用 match 方法來匹配字符串,結果是一個數組,而不是最後的匹配結果,爲何呢?看下面的例子
"abc".match(/(a)(b)(c)/) // ["abc", "a", "b", "c"]
"abc".match(/abc/) // ["abc"]

能夠看到,括號會緩存括號裏匹配的內容,單獨列出來,那麼怎麼拿到括號內的內容呢,而不是經過 match 返回的結果拿,由於有時候咱們須要在表達式裏使用捕獲的值,從而達到匹配重複的內容。這部分就叫反向引用

"abc-abc-cba".replace(/(a)(b)c-\1\2/, '') // c-cba
"abc-abc-cba".replace(/(a)(b)c/g, '$1$2') // ab-ab-cba
RegExp.$1 // a
RegExp.$2 // b

這裏展現了兩種使用反向引用的方法,一種是在表達式內經過 \1\2 的形式拿到兩個緩存的值,一種是使用 $1$2的形式拿到。由於正則是從左開始匹配的,因此 (a) 就是第一個捕獲的匹配值,因此他是\1 或是 $1,以此類推。

非捕獲型括號

上面說了括號會捕獲值,通常來講這樣會影響性能,或者你會用到括號來作分組,可是不想捕獲的狀況,(?:)非捕獲型括號就是這麼用的,那麼重寫一下上面的例子。

"abc-abc-cba".replace(/(a)(?:b)c-\1\2/, '') // 匹配失敗了,由於\2不存在
"abc-abc-cba".replace(/(a)(?:b)c-\1/, '') // bc-cba
RegExp.$1 // a
RegExp.$2 // ""

環視

類型 正則表達式
確定逆序環視 ?<=
否認逆序環視 ?<!
確定順序環視 ?=
否認順序環視 ?!

?=?! 在菜鳥和w3school 裏有簡單的說起,菜鳥裏還提到這兩個還能重寫捕獲,可是 ?<=?<! 並無說起。

寫幾個 demo 表示一下:

// 找一個字母 a ,它緊跟在 b 前面
"abac".replace(/a(?=b)/g, '') // bac

// 找到一個字母 a ,它緊跟在一個不是 b 的字母前面
"abac".replace(/a(?!b)/g, '') // abc

// 接着是逆序環視
// 找到一個字母 a ,它跟在 b 後面
"abac".replace(/(?<=b)a/g, '') // abc

// 找到一個字母 a ,他不跟在 b 後面
"abac".replace(/(?<!b)a/g, '') // bac

// 一個有趣匹配
// 在 a 和 b 之間插入一個 ","
"abac".replace(/(?<=a)(?=b)/g, ",") // a,bac

能夠看出,環視是要和捕獲括號一塊兒用的,而且不會佔用匹配字符,他只是檢查表達式是否匹配。因此這就是重寫捕獲了。

忽略優先量詞

量詞匹配通常有三種 *+?。然而還能夠寫做, *?+? ,使匹配結果導向徹底不一樣的結果。例子:

"abc-aaa-abc-abc".replace(/abc-.*-abc/, '') // ""

"abc-aaa-abc-abc".replace(/abc-.*?-abc/, '') // "-abc"

*? 忽略優先會先忽略當前匹配的值,先匹配後面的 -abc,若是匹配失敗,再匹配本身,而 *會優先匹配本身,等匹配結束以後,再從後面一點點吐出,回來匹配量詞後面的表達式。從而形成以上不一樣的結果。知道這個以後,就不會再傻傻的把 *? 分開解讀了。固然,具體狀況具體分析,到底使用哪一個。

回溯

正則也是會有性能問題的,若是表達式寫的很差,形成過分的災難性回溯,會致使執行時間指數級增加。具體狀況仍是經過搜索引擎瞭解吧,解釋起來會過長,並且做爲正則新手的我還不必定能解釋清楚。。。

最後

以上是我在《精通正則表達式》一書中獲得的一些收穫,但願能分享給你們,若有錯誤歡迎指正。下一步呢就是去作一些練習來鞏固一下了。

相關文章
相關標籤/搜索