前面我們提到到了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+)?
當時看着比較舒服,但是在測試中,我發現了嚴重的問題,能空匹配,簡單說就是任何空字符串都能匹配成功。
這是一個嚴重的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+ 進行匹配了,所以整數可以匹配成功。
這個正則霸氣在於利用最少的代碼實現最優的性能,當然整數情況回溯是必須的,不過性能上也不會有多大的問題。
我們來看個測試吧:
var str1 = "123, 1.23, 1.2e3, 1.2e+3, 1.2e-3, .123, .12e3, .12e+3, .12e-3"; var str2 = "123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123"; var re1 = /\d+(?:\.\d+)?(?:[eE][+-]?\d+)?|\.\d+(?:[eE][+-]?\d+)?/g; var re11= new RegExp(re1.source, "g"); var re2 = /(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/g; var re22= new RegExp(re2.source, "g"); console.log( str1.match(re1).toString() === str1.match(re22).toString(), str2.match(re1).toString() === str2.match(re22).toString() ); test(str1, re1, "str1 re1")(str1, re11, "str1 re11") (str1, re2, "str1 re2")(str1, re22, "str1 re22"); test(str2, re1, "str2 re1")(str2, re11, "str2 re11") (str2, re2, "str2 re2")(str2, re22, "str2 re22"); function test(str, re, name) { console.time(name); for (var i=0; i<1e6; ++i) str1.match(re); console.timeEnd(name); return test; }
對浮點和整數進行100萬次匹配測試,可以看到浮點數測試相差0.1秒,整數相差0.2秒。
這已經是非常小的性能差異了,有的垃圾正則,100萬次測試可能會相差十幾甚至幾十秒呢。
這么個小知識點讓我開拓了眼界,其實只是寫的少,看的少,所以一直都是井底蛙,技術這東西,必須多看,多想,多寫才行。
好了今天的分享完畢,明天見。