通用 HTML 標簽區配正則
最近看網站日志,發現有人在博客上轉了我不知道幾年前寫的一個匹配 HTML 標簽的正則,剛好最近也在做一些相關的事情,頓時來了興趣。就拿回來改改,成了下面這樣,可能會有一些 case 遺漏,歡迎修改,已知在內嵌 <script> 復雜內容的處理能力較弱,不過對純 HTML 來說已經夠用,拿來做一些分析工具還是不錯滴。
<script type="text/javascript">
var str = "<br /><br/><br><br ><中文><div><div id=a>無憂腳本<img src=/"http://bbs.51js.com/images/default/logo.gif/" width=/"191/" height='75' border=0 onload=/"if(testver>0 && testver<500)alert('test');/" /n onerror='alert(/"test/")' /><img src=xxx alt=/"hello/njust a test!/"></div><hr ><script type=/"test/javascript/" defer>alert(/"just a test!/");<//script>Hello.<input type=text value=/"無憂腳本/"><br / ><img /"/" ></ ><!-- 注釋 -->< ucren><!-- 再<注>釋 --><img alt=' title='/"' /><b>123</b>1<2<3,3<4>1<b><img src=/"http://bbs.51js.com/images/old51js/logo.gif/" /><!-- 三注釋>>> -->";
var reg = /<(?:(?://?[A-Za-z]/w*/b(?:[=/s](['"]?)[/s/S]*?/1)*)|(?:!--[/s/S]*?--))//?>/g;
alert(str.match(reg).join("/n----------------------------------------------------/n"));
</script>
有朋友留言說Java直接使用的話會報錯。我后來查了一下,發現Java正則引擎支持的特性相對比較少。在1.6版本中不能使用命名組(貌似1.7的時候開始支持了),否則會報以下錯誤,更別說平衡組了。因此感覺要實現無限級的嵌套匹配不大現實。
java.util.regex.PatternSyntaxException: Look-behind group does not have an obvious maximum length near index XX
在網上搜了好久也沒找到完美的解決方案。不過,我們可以實現有限級Html嵌套標簽匹配。思路相對于無限級來說就簡單了好多,不需要那么多高級的特性。
示例:
<div id='container'><BR> <div style='background-color:gray;' id='footer'><BR> <a id='gotop' href='#' onclick='MGJS.goTop();return false;'>Top</a><BR> <a id='powered' >WordPress</a><BR> <div id='copyright'><BR> Copyright © 2009 簡單生活 ―― Kevin Yang的博客<BR> </div><BR> <div id='themeinfo'><BR> Theme by <a >mg12</a>. Valid <a >XHTML 1.1</a><BR> and <a >CSS 3</a>.<BR> </div><BR> </div><BR></div>
在上面這個示例中,我們打算匹配id為footer的這個嵌套div,而且假設我們預先知道footer這個div里面最多只會嵌套一級div。更多級的情況我們一會兒再講。
footer的開始和結束標簽匹配很簡單:
<div [^>]*id='footer'[^>]*>......(這里的省略號是一會要填寫的)</div>
夾在開始和結束標簽之間的內容無非有兩種情況:
內容A: div標簽,并且此div內無嵌套div
內容B: 任意其他內容
然后就是這兩種內容的不斷重復而已。正則表示如下:
(<div[^>]*>.*?</div>|.)*?
注意最后面的問號必須要加上,否則由于正則的貪婪匹配特性,footer的閉合標簽會匹配失誤。
OK了,匹配最多嵌套一級div的正則表達式如下:
<div [^>]*id='footer'[^>]*>(<div[^>]*>.*?</div>|.)*?</div>
那么如果footer標簽里頭最多會嵌套兩級div的話怎么辦呢?
其實也不難,我們只需要把上面的“內容A”部分中的點號稍作替換即可。修改如下:
<div [^>]*id='footer'[^>]*>(<div[^>]*>(<div[^>]*>.*?</div>|.)*?</div>|.)*?</div>
到這里你可能就知道,如果要匹配最多嵌套三級div的話,正則應該怎么寫了:
<div [^>]*id='footer'[^>]*>(<div[^>]*>(<div[^>]*>(<div[^>]*>.*?</div>|.)*?</div>|.)*?</div>|.)*?</div>
所以實際上,只要你的html結構不是特別復雜的話,也就是說嵌套不會很深的話,那么你完全可以使用這種方式來匹配嵌套html標簽。
這個正則在Java和Javascript中都可以使用,因為它沒有用到任何高級特性。