標籤:c style class blog code java
原文:再議 js 數字格式之Regex
前面我們提到到了js的數字格式《淺談 js 數字格式類型》,之前的《js 正則練習之文法高亮》裡也提到了最佳化數字匹配的正則。
不過最近落葉給了我一個正則,讓我豁然開朗,比我寫的犀利多了,所以今天拿出來簡單說一下(只說十進位部分的匹配)。
先看下我之前寫的正則:/\d+(?:\.\d+)?(?:[eE][+-]?\d+)?|\.\d+(?:[eE][+-]?\d+)?/
落葉在 jQuery 中發現的正則: /(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/ (ps: 我去掉了 [+-] 因為沒必要匹配那個。。)
很明顯犀利很多。
我的思路其實很簡單,就是根據官方描述然後寫了個臃腫不堪的正則。
在 MDN JavaScript Guide 的 Values, variables, and literals#Floating-point literals 一節中可以看到。
對於js數字格式的文法描述為 [(+|-)][digits][.digits][(E|e)[(+|-)]digits] (PS:這不是正則)
所以我就粗略的寫了一個運算式 (?:\d+)?(?:\.\d+)?(?:[eE][+-]?\d+)?
當時看著比較舒服,但是在測試中,我發現了嚴重的問題,能空匹配,簡單說就是任何Null 字元串都能匹配成功。
這是一個嚴重的BUG,所以我把它拆分為兩部分,修複了這個BUG,於是得到了上面那個臃腫的代碼,沒辦法水平有限。
其實是我想的太簡單了,我只是按照傳統的想法寫的正則,先匹配整數,然後匹配小數,最後匹配指數。。。
再看 jQuery 中的正則 /(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/ 寫的太霸氣了。
他的思路是先匹配浮點數,然後匹配小數點後面的整數,接著匹配指數,同樣是3部分,只是匹配順序不同而已。
當然如果匹配不到浮點數,就回溯放棄匹配,直接匹配整數和指數部分。
這樣就不需要拆分成兩個運算式了。
我們做個測試,先去掉 | 試試,測試資料如下:
?
123 1.23 1.2e3 1.2e+3 1.2e-3 .123 .12e3 .12e+3 .12e-3 |
正則: /(?:\d*\.)\d+(?:[eE][+-]?\d+|)/
發現 123 沒匹配到,後面的浮點型資料都可以正常匹配。
為什麼會這樣呢,因為 (?:\d*\.) 是必須匹配到 . 的,所以整數就無法匹配成功了。
(?:\d*\.|) 則可以在匹配失敗的情況下回溯然後發現第二個運算式是個空,就是不匹配了。
自然就讓出位置給後面的 \d+ 進行匹配了,所以整數可以匹配成功。
這個正則霸氣在於利用最少的代碼實現最優的效能,當然整數情況回溯是必須的,不過效能上也不會有多大的問題。
我們來看個測試吧。
對浮點和整數進行100萬次匹配測試,可以看到浮點數測試相差0.1秒,整數相差0.2秒。
這已經是非常小的效能差異了,有的垃圾正則,100萬次測試可能會相差十幾甚至幾十秒呢。
這麼個小知識點讓我開拓了眼界,其實只是寫的少,看的少,所以一直都是井底蛙,技術這東西,必須多看,多想,多寫才行。
好了今天的分享完畢,明天見。