正則匹配中的非貪婪匹配不是最短匹配

最近在工作中遇到一個需求,就是找出html中所有錨文字包含 聯繫方式 的超鏈接。剛開始我寫了一個很簡單的正則來解決這個問題<a.*?聯繫方式.*?</a。但是在測試的時候卻發現這個正則表達式並不像我想象的那樣工作。

圖中給出了一個正則表達式匹配的例子,可以看出在這段文字中有兩個匹配,但是第一個匹配所包含的結果已經超出了實際需要的範圍,包含了太多的超鏈接標籤,而我需要的是最短的匹配也就是圖中橫線畫出的範圍,這是怎麼回事?

正則匹配的原理

這要從正則匹配的原理說起,簡單的來說正則匹配是一種貪心的算法。它總是先找到第一個匹配的位置,然後向後繼續匹配其他的表達式符號。對於本文給出的正則表達式,會現在html中找到一個<a標籤,然後之後是.*?,直到找到一個聯繫方式,在這個過程中如果找到了另一個<a,會被當作.*?匹配的部分,而忘記了要匹配表達式的開頭就是<a。也就是說,除非發生失配,正則表達式不會主動地回溯。儘管使用了來表達非貪婪匹配,也只能限制向後匹配時盡可能地短,而不能縮短已匹配部分的長度。也就是說,非貪婪匹配向後是最短匹配,但是向前不是最短匹配

對於這個任務,我後來使用了其他效率更高的方法實現了,但是有沒有可能使用正則表達式來完成這個任務呢?

零寬斷言

零寬斷言是一種零寬度的匹配,它匹配到的內容不會保存到匹配結果中去,最終匹配結果只是一個位置而已。
作用是給指定位置添加一個限定條件,用來規定此位置之前或者之後的字符必須滿足限定條件才能使正則中的字表達式匹配成功。

零寬斷言總共有四種

對於這個需求,實際上應該找到離聯繫方式最近的一個<a,也就是說,在<a聯繫方式之前不能再有其他的<a了。而最開始的正則匹配表達式<a.*?聯繫方式.*?</a中的.*?可以通過.來匹配任意一個字符,在這裏可以使用零寬度負先行斷言來限制.匹配的任何一個字符的右側不能夠再有<a。也就是.(?!<a),再將這個整體重複多次(.(?!<a))*?。這裏引入了一個額外的括號,為了不產生多餘的匹配,可以使用非捕獲組來去除不需要的匹配,最終可以將整個表達式寫成<a(:?.(?!<a))*?聯繫方式.*?</a

可以看到匹配的範圍已經縮小到最後一個出現的超鏈接。

總結

因為正則表達式實現原理的限制,儘管選擇非貪婪匹配,匹配到的結果也不一定是最短的匹配。
通常正則表達式總是表明了“要匹配什麼”,而通過零寬度負斷言,則可以表明“不匹配什麼”,這比字符集中使用^來取反更加強大。

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※教你寫出一流的銷售文案?

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

※回頭車貨運收費標準

※別再煩惱如何寫文案,掌握八大原則!

※超省錢租車方案

您可能也會喜歡…