記錄正則表達式引起的血案

遇到問題

前些日子在作線上驗品抽檢需求,在開發發起檢測任務頁面時遇到一個字段校驗的訴求,具體要求:「用戶能夠填入多組 ID,每組 ID 能夠由商品 ID 和 SKU ID 共同構成,也能夠只由其中一種 ID 構成,兩種 ID 之間經過冒號分隔;每組 ID 之間又須要經過英文逗號分隔」。這不就是一個表單字段格式校驗場景嗎?所以很天然的想到使用正則表達式來處理,在搗騰了幾分鐘以後寫好一個正則表達式:html

/^(((d+:d+)+)|(,(d+:d+)+)|(d)+|(,(d+)+)){0,}$/

在 chrome 瀏覽器控制檯簡單測試了一下順利經過!很完美~ image.png 但是打包發到平常環境後,測試妹子找到我反饋說「這個頁面很卡,並且是卡死的那種!」。git

定位問題

通過排查後發現每次都是填入了商品 ID 以後發生的頁面假死,因而判定問題應該是出在表單字段的 validator 函數,當我把這個 validator 函數註釋以後發現頁面恢復正常!可是這個 validator 函數是 formily 提供出來的鉤子函數,鉤子函數自己不會有問題,並且我在鉤子函數裏面的自定義邏輯也很簡單,所以能夠篤定問題出在正則表達式判斷這裏! image.png 可是我在 chrome 控制檯裏面明明測試的很絲滑,怎麼會出現卡死呢?!因而讓測試妹子把用例發我一看:github

10253:23091,10253:23091,10253:23091,10253:23091,10253:23091,10253:23091,10253:23091,10253:23091,10253:23091,10253:23091,10253:23091,10253:23091,10253:23091,10253:23091,10253:23091正則表達式

而後再把前面那段正則表達式語句進行可視化分析: image.png 原來是我本身的用例太過簡單,根本沒有觸發到正則的性能問題。之前一直據說正則表達式可能會形成性能問題,但因爲一直都沒有真正遇到過,因此也沒有真正上心去了解正則執行原理,因而乎觸發到知識盲區形成這個 bug。chrome

如何解決

問題發生的緣由雖然被定位到,可是我不太可能在短期內寫出一個符合需求的高性能正則表達式,由於測試妹子還等着我修掉 bug 後繼續往下走。因而我準備先用一個臨時方案來解決,臨時方案的思路:把當前這個複雜度大的校驗規則,分解爲幾個簡單的校驗規則,再把他們一塊兒的結果串聯起來。具體實現以下圖: image.png 把用戶填入的很長很複雜的字符串按照英文逗號爲分隔符的規則先進行分組,而後再用一個簡單的正則表達式去判斷每組 ID 的格式是否知足,由於每組 ID 的長度都不長,因此不會觸發性能問題。 在解決燃眉之急後,我就開始蒐集正則表達式相關的學習資料和博客,在 ATA 上搜索到好幾篇與個人問題高度類似的文章《一個由正則表達式引起的血案》、《一個正則表達式flag引起的血案》、《正則表達式的RegExp.test函數》,可是這些文章各有側重點,而我指望的是更加系統和全面的學習資料。通過一番搜索後,終於在豆瓣上發現了《精通正則表達式》這本書。不過我本身目前還沒啃完,因此下面不會講原理,等後面看完了再來寫個讀後感和引擎原理分析~ 在看書的過程當中我發現一個新問題:不是每一個業務場景都須要咱們寫出一個高性能的正則表達式,咱們在開發業務的時候更多想要的是「如何快速發現性能低劣的正則表達式?」——簡而言之,有一個工具能在咱們編寫代碼的過程當中去分析正則表達式的性能並給出反饋!express

工具沉澱

帶着上面的想法我去調研了 vscode 插件和 eslint 插件:npm

  • vscode 插件生態中有不少 regex 相關的插件,不過大都是集中在如何快速預覽和如何快速提供經常使用的正則表達式
  • eslint 插件生態提供了 eslint-plugin-optimize-regexeslint-plugin-vuln-regex-detector,前者主要是在對現有的正則表達式提供優化建議,沒有作回溯風險校驗;後者只作了回溯風險校驗,而且好久沒有維護了

vscode 插件這條路相比於 eslint 插件要更重一些,並且很差落進團隊規範裏面,所以決定自研一個 eslint 插件集優化建議和回溯風險於一體。 在正式落代碼以前須要弄清楚兩個問題:瀏覽器

  • 如何度量正則表達式複雜度?
  • 如何智能地給出優化建議?

這兩個問題對於正則入門級選手的我來講實在是太難,通過一番調研以後發現早就有前輩在研究這些問題而且給出了一套理論依據——star height,以及一個成熟的正則表達式處理工具——regexp-tree。果真站在巨人的肩膀上不只看得又高又遠,並且代碼擼得也飛快~ 花了半天時間就完成了 eslint-plugin-analyze-regex 插件的第一個版本,運行效果以下: image.pngimage.pngide

總結感想

  • 關於我的:若是我對正則表達式很熟悉而且沒有知識盲區,那就不會遇到這個問題,也就不會去看相關資料,同時也不會感受到啃書的效率低,更不會想到用 eslint 插件來下降正則表達式使用成本
  • 關於工具:本質上是藉助 regexp-tree 提供的解析、優化能力來實現核心功能,而後再以 eslint 插件的形式來透出。對於開發者而言,毫無違和感而且開發體驗一致

雖然在解決問題的整個過程當中,有不少技術點(如:正則引擎、回溯、星高問題、regex-tree 原理、eslint 原理及插件機制)沒有深刻進去吃透並扒出來細講,可是與正則相關的一些知識面逐漸擴充,這也算是一種收穫吧!函數

參考文獻

做者:ES2049 / 凡哥

文章可隨意轉載,但請保留此原文連接。
很是歡迎有激情的你加入 ES2049 Studio,簡歷請發送至 caijun.hcj@alibaba-inc.com 。

相關文章
相關標籤/搜索