1. 程式人生 > 其它 >sass的安裝及使用

sass的安裝及使用

作者:騰訊技術工程
連結:https://www.zhihu.com/question/48219401/answer/1476436385
來源:知乎
著作權歸作者所有。商業轉載請聯絡作者獲得授權,非商業轉載請註明出處。

分享一篇騰訊前端開發工程師@mathe寫的正則表示式教程。

正則表示式具有偉大技術發明的一切特點,它簡單、優美、功能強大、妙用無窮。對於很多實際工作來講,正則表示式簡直是靈丹妙藥,能夠成百倍地提高開發效率和程式質量。

1. 正則常見規則

1.1 字元匹配

1.2 位置匹配

1.3 組

1.4 先行斷言

1.5 後行斷言

1.6 量詞和分支

以下都是惰性匹配
{m,n}?
{m,}?
??
+?
*?

1.7 分支

1.8 修飾符

2. 運算子優先順序

3. 正則回溯

3.1 什麼是回溯演算法

以下是來自摘自維基百科的部分解釋:

回溯法是一種通用的計算機演算法,用於查詢某些計算問題的所有(或某些)解決方案,特別是約束滿足問題,逐步構建候選解決方案,並在確定候選不可能時立即放棄候選("回溯")完成有效的解決方案。

回溯法通常用最簡單的遞迴方法來實現,在反覆重複上述的步驟後可能出現兩種情況:
  • 找到一個可能存在的正確的答案
  • 在嘗試了所有可能的分步方法後宣告該問題沒有答案
在最壞的情況下,回溯法會導致一次複雜度為指數時間的計算。

3.2 什麼是正則回溯

正則引擎主要可以分為兩大類:一種是 DFA(Deterministic finite automaton 確定型有窮自動機),另一種是 NFA(NFA Non-deterministic finite automaton  非確定型有窮自動機)。NFA 速度較 DFA 更慢,並且實現複雜,但是它又有著比 DFA 強大的多的功能,比如支援反向引用等。像 javaScript、java、php、python、c#等語言的正則引擎都是 NFA 型,NFA 正則引擎的實現過程中使用了回溯。

3.2.1 沒有回溯的正則

舉一個網上常見的例子,正則表示式/ab{1,3}c/g 去匹配文字'abbc',我們接下來會通過 RegexBuddy 分析其中的匹配過程,後續的一個章節有關於 RegexBuddy 的使用介紹。

如上圖所示,讓我們一步一步分解匹配過程:

  1. 正則引擎先匹配 a。
  2. 正則引擎儘可能多地(貪婪)匹配 b。
  3. 正則引擎匹配 c,完成匹配。

在這之中,匹配過程都很順利,並沒發生意外(回溯)。

3.2.2 有正則回溯的正則

讓我們把上面的正則修改一下,/ab{1,3}c/g 改成/ab{1,3}bc/g,接下再通過 RegexBuddy 檢視分析結果。

我們再一步一步分解匹配過程:

  1. 正則引擎先匹配 a。
  2. 正則引擎儘可能多地(貪婪)匹配 b{1,3}中的 b。
  3. 正則引擎去匹配 b,發現沒 b 了,糟糕!趕緊回溯!
  4. 返回 b{1,3}這一步,不能這麼貪婪,少匹配個 b。
  5. 正則引擎去匹配 b。
  6. 正則引擎去匹配 c,完成匹配。

以上,就是一個簡單的回溯過程。

3.3 正則回溯的幾種常見形式

從上面發生正則回溯的例子可以看出來,正則回溯的過程就是一個試錯的過程,這也是回溯演算法的精髓所在。回溯會增加匹配的步驟,勢必會影響文字匹配的效能,所以,要想提升正則表示式的匹配效能,瞭解回溯出現的場景(形式)是非常關鍵的。

3.3.1 貪婪量詞

在 NFA 正則引擎中,量詞預設都是貪婪的。當正則表示式中使用了下表所示的量詞,正則引擎一開始會盡可能貪婪的去匹配滿足量詞的文字。當遇到匹配不下去的情況,就會發生回溯,不斷試錯,直至失敗或者成功。

ble data-draft-node="block" data-draft-type="table" data-size="normal" data-row-style="normal">

當多個貪婪量詞挨著存在,並相互有衝突時,秉持的是"先到先得"的原則,如下所示:

let string = "12345";

let regex = /(\d{1,3})(\d{1,3})/;
console.log( string.match(regex) );
// => ["12345", "123", "45", index: 0, input: "12345"]

3.3.2 惰性量詞

貪婪是導致回溯的重要原因,那我們儘量以懶惰匹配的方式去匹配文字,是否就能避免回溯了呢?答案是否定的。

讓我們還是看回最初的例子,/ab{1,3}c/g 去匹配 abbc。接下來,我們再把正則修改一下,改成/ab{1,3}?c/g 去匹配 abbc,以懶惰匹配的方式去匹配文字,RegexBuddy 執行步驟如下圖所示:

  1. 正則引擎先匹配 a。
  2. 正則引擎儘可能少地(懶惰)匹配 b{1,3}中的 b。
  3. 正則引擎去匹配 c,糟糕!怎麼有個 b 擋著,匹配不了 c 啊!趕緊回溯!
  4. 返回 b{1,3}這一步,不能這麼懶惰,多匹配個 b。
  5. 正則引擎再去匹配 c,糟糕!怎麼還有 b 擋著,匹配不了 c 啊!趕緊回溯!
  6. 返回 b{1,3}這一步,不能這麼懶惰,再多匹配個 b。
  7. 正則引擎再去匹配 c,匹配成功,棒棒噠!

本來是好端端不會發生回溯的正則,因為使用了惰性量詞進行懶惰匹配後,反而產生了回溯了。所以說,惰性量詞也不能瞎用,關鍵還是要看場景。

3.3.3 分組

分支的匹配規則是:按照分支的順序逐個匹配,當前面的分支滿足要求了,則捨棄後面的分支。

舉個簡單的分支栗子,使用正則表示式去匹配 /abcde|abc/g 文字 abcd,還是通過 RegexBuddy 檢視執行步驟:

  1. 正則引擎匹配 a。
  2. 正則引擎匹配 b。
  3. 正則引擎匹配 c。
  4. 正則引擎匹配 d。
  5. 正則引擎匹配 e,糟糕!下一個並不是 e,趕緊回溯!
  6. 上一個分支走不通,切換分支,第二個分支正則引擎匹配 a。
  7. 第二個分支正則引擎匹配 b。
  8. 第二個分支正則引擎匹配 c,匹配成功!

由此,可以看出,分組匹配的過程,也是個試錯的過程,中間是可能產生回溯的。

4. 正則的分析與除錯

RegexBuddy 是個十分強大的正則表示式學習、分析及除錯工具。RegexBuddy 支援 C++、Java、JavaScript、Python 等十幾種主流程式語言。通過 RegexBuddy,能看到正則一步步建立的過程。結合測試文字,你能看到正則一步步執行匹配的過程,這對於理解正則回溯和對正則進行進一步優化,都有極大的幫助。

4.1 安裝分析除錯工具

可以在 RegexBuddy 的官方網站下載及獲取 RegexBuddy。

下載完後,一步步點選安裝即可。

4.2 工具介面介紹

下圖便是 RegexBuddy 介面的各個面板及相關功能。

4.3 建立正則

為了方便使用,可以在佈局設定那裡將佈局設定成 Side by Side Layout。

在正則輸入區輸入你的正則 regex1,檢視 Create 面板,就會發現面板上顯示了正則的建立過程(或者說是匹配規則),在 Test 面板區域輸入你的測試文字,滿足 regex1 匹配規則的部分會高亮顯示,如下圖所示。

4.4 使用 RegexBuddy 的 Debug 功能

選中測試文字,點選 debug 就可以進入 RegexBuddy 的 debug 模式,個人覺得這是 RegexBuddy 最強大地方,因為它可以讓你清楚地知道你輸入的正則對測試文字的匹配過程,執行了多少步,哪裡發生了回溯,哪裡需要優化,你都能一目瞭然。

4.5 使用 RegexBuddy 的 Library 功能

RegexBuddy 的正則庫內建了很多常用正則,日常編碼過程中需要的很多正則表示式都能在該正則庫中找到。

4.6 更多工具推薦

5. 正則效能優化

正則是個很好用的利器,如果使用得當,如有神助,能省掉大量程式碼。當如果使用不當,則是處處埋坑。所以,本章節的重點就是總結如何寫一個高效能的正則表示式。

5.1 避免量詞巢狀

舉個簡單的例子對比:

我們使用正則表示式/a*b/去匹配字串 aaaaa,看下圖 RegexBuddy 的執行過程:

我們將以上正則修改成/(a*)*b/去匹配字串 aaaaa,再看看 RegexBuddy 的執行結果過程:

以上兩個正則的基本執行步驟可以簡單認為是:

  1. 貪婪匹配
  2. 回溯
  3. 直至發現匹配失敗

但令人驚奇的是,第一個正則的從開始匹配到匹配失敗這個過程只有 14 步。而第二個正則卻有 128 步之多。可想而知,巢狀量詞會大大增加正則的執行過程。因為這其中進行了兩層回溯,這個執行步驟增加的過程就如同演算法複雜度從 O(n)上升到 O(n^2)的過程一般。

所以,面對量詞巢狀,我們需作出適當的轉化消除這些巢狀:

(a*)* <=> (a+)* <=> (a*)+ <=> a*
(a+)+ <=> a+

5.2 使用非捕獲組

NFA 正則引擎中的括號主要有兩個作用:

  1. 主流功能,提升括號中內容的運算優先順序
  2. 反向引用

反向引用這個功能很強大,強大的代價是消耗效能。所以,當我們如果不需要用到括號反向引用的功能時,我們應該儘量使用非捕獲組,也就是:

// 捕獲組與非捕獲組
() => (?:)

5.3 分支優化

分支也是導致正則回溯的重要原因,所以,針對正則分支,我們也需要作出必要的優化。

5.3.1 減少分支數量

首先,需要減少分支數量。比如不少正則在匹配 http 和 https 的時候喜歡寫成:

/^http|https/

其實上面完全可以優化成:

/^https?/

這樣就能減少沒必要的分支回溯

5.3.2 縮小分支內的內容

縮小分支中的內容也是很有必要的,例如我們需要匹配 this 和 that ,我們也許會寫成:

/this|that/

但上面其實完全可以優化成

/th(?:is|at)/

有人可能認為以上沒啥區別,實踐出真知,讓我們用以上兩個正則表示式去匹配一下 that。

我們會發現第一個正則的執行步驟比第一個正則多兩步,那是因為第一個正則的回溯路徑比第二個正則的回溯路徑更長了,最終導致執行步驟變長。

5.4 錨點優化

在能使用錨點的情況下儘量使用錨點。大部分正則引擎會在編譯階段做些額外分析, 判斷是否存在成功匹配必須的字元或者字串。類似^、$ 這類錨點匹配能給正則引擎更多的優化資訊。

例如正則表示式 hello(hi)?$ 在匹配過程中只可能從字串末尾倒數第 7 個字元開始, 所以正則引擎能夠分析跳到那個位置, 略過目標字串中許多可能的字元, 大大提升匹配速度。

6. 結語

曾經有一次因為寫一個性能惡劣的正則表示式,導致程式碼執行過程因為效能問題掛掉。於是下定決心要把正則表示式搞明白,看了不少文章書籍,做了不少練習之後,總算摸到了些門道,也真真切切體會到正則表示式的優美和強大。寫下此文,記錄下一些學習心得和總結,望批評指正,共同進步。

7. 參考