回覆列表
-
1 # 江蘇高科中維
-
2 # 檸檬愛民謠
1.不用正則。例如想要驗證非負數之類的。直接parse一下然後判斷不就行了。幹嘛非寫正則啊。2.少用lazy。懶惰式的.+?這種的計算代價比貪婪式的.+高。如果知道具體的限制大小,用{x,y}效率會更高。3.如果知道具體範圍。儘量自己書寫範圍,避免用"."。以上只是個人經驗。另外剛剛所謂的效率高說的是執行效率方面。從開發效率高的角度上來說,個人認為可讀性好的易於分割除錯的效率高。畢竟,正則的可讀性和除錯性是備受詬病的。
如果純粹是為了挑戰自己的正則水平,用來實現一些特效(例如使用正則表示式計算質數、解線性方程),效率不是問題;如果所寫的正則表示式只是為了滿足一兩次、幾十次的執行,最佳化與否區別也不太大。但是,如果所寫的正則表示式會百萬次、千萬次地執行,效率就是很大的問題了。我這裡總結了幾條提升正則表示式執行效率的經驗(工作中學到的,看書學來的,自己的體會),貼在這裡。如果您有其它的經驗而這裡沒有提及,歡迎賜教。
為行文方便,先定義兩個概念。
誤匹配:指正則表示式所匹配的內容範圍超出了所需要範圍,有些文字明明不符合要求,但是被所寫的正則式“擊中了”。例如,如果使用\d{11}來匹配11位的手機號,\d{11}不單能匹配正確的手機號,它還會匹配98765432100這樣的明顯不是手機號的字串。我們把這樣的匹配稱之為誤匹配。
漏匹配:指正則表示式所匹配的內容所規定的範圍太狹窄,有些文字確實是所需要的,但是所寫的正則沒有將這種情況囊括在內。例如,使用\d{18}來匹配18位的身份證號碼,就會漏掉結尾是字母X的情況。
寫出一條正則表示式,既可能只出現誤匹配(條件寫得極寬鬆,其範圍大於目標文字),也可能只出現漏匹配(只描述了目標文字中多種情況種的一種),還可能既有誤匹配又有漏匹配。例如,使用\w+\.com來匹配.com結尾的域名,既會誤匹配abc_.com這樣的字串(合法的域名中不含下劃線,\w包含了下劃線這種情況),又會漏掉ab-c.com這樣的域名(合法域名中可以含中劃線,但是\w不匹配中劃線)。
精準的正則表示式意味著既無誤匹配且無漏匹配。當然,現實中存在這樣的情況:只能看到有限數量的文字,根據這些文字寫規則,但是這些規則將會用到海量的文字中。這種情況下,儘可能地(如果不是完全地)消除誤匹配以及漏匹配,並提升執行效率,就是我們的目標。本文所提出的經驗,主要是針對這種情況。
掌握語法細節。正則表示式在各種語言中,其語法大致相同,細節各有千秋。明確所使用語言的正則的語法的細節,是寫出正確、高效正則表示式的基礎。例如,perl中與\w等效的匹配範圍是[a-zA-Z0-9_];perl正則式不支援肯定逆序環視中使用可變的重複(variable repetition inside lookbehind,例如(?<=.*)abc),但是.Net語法是支援這一特性的;又如,JavaScript連逆序環視(Lookbehind,如(?<=ab)c)都不支援,而perl和python是支援的。《精通正則表示式》第3章《正則表示式的特性和流派概覽》明確地列出了各大派系正則的異同,這篇文章也簡要地列出了幾種常用語言、工具中正則的比較。對於具體使用者而言,至少應該詳細瞭解正在使用的那種工作語言里正則的語法細節。
先粗後精,先加後減。使用正則表示式語法對於目標文字進行描述和界定,可以像畫素描一樣,先大致勾勒出框架,再逐步在局步實現細節。仍舉剛才的手機號的例子,先界定\d{11},總不會錯;再細化為1[358]\d{9},就向前邁了一大步(至於第二位是不是3、5、8,這裡無意深究,只舉這樣一個例子,說明逐步細化的過程)。這樣做的目的是先消除漏匹配(剛開始先儘可能多地匹配,做加法),然後再一點一點地消除誤匹配(做減法)。這樣有先有後,在考慮時才不易出錯,從而向“不誤不漏”這個目標邁進。
留有餘地。所能看到的文字sample是有限的,而待匹配檢驗的文字是海量的,暫時不可見的。對於這樣的情況,在寫正則表示式時要跳出所能見到的文字的圈子,開拓思路,作出“戰略性前瞻”。例如,經常收到這樣的垃圾簡訊:“發*票”、“發#漂”。如果要寫規則遮蔽這樣煩人的垃圾簡訊,不但要能寫出可以匹配當前文字的正則表示式 發[*#](?:票|漂),還要能夠想到 發.(?:票|漂|飄)之類可能出現的“變種”。這在具體的領域或許會有針對性的規則,不多言。這樣做的目的是消除漏匹配,延長正則表示式的生命週期。
明確。具體說來,就是謹慎用點號這樣的元字元,儘可能不用星號和加號這樣的任意量詞。只要能確定範圍的,例如\w,就不要用點號;只要能夠預測重複次數的,就不要用任意量詞。例如,寫析取twitter訊息的指令碼,假設一條訊息的xml正文部分結構是<span class=”msg”>…</span>且正文中無尖括號,那麼<span class=”msg”>[^<]{1,480}</span>這種寫法的思路要好於<span class=”msg”>.*</span>,原因有二:一是使用[^<],它保證了文字的範圍不會超出下一個小於號所在的位置;二是明確長度範圍,{1,480},其依據是一條twitter訊息大致能的字元長度範圍。當然,480這個長度是否正確還可推敲,但是這種思路是值得借鑑的。說得狠一點,“濫用點號、星號和加號是不環保、不負責任的做法”。
不要讓稻草壓死駱駝。每使用一個普通括號()而不是非捕獲型括號(?