正則表達(dá)式7種高級(jí)應(yīng)用技巧教程
本文是一篇正則表達(dá)式高級(jí)教程,主要通過對(duì)正則表達(dá)式幾個(gè)概念的介紹,深入探討正則表達(dá)式高級(jí)功能,以期達(dá)到通俗化解釋正則表達(dá)式高深概念的目的。
介紹過正則表達(dá)式的基礎(chǔ)和基本套路正則三段論:定錨點(diǎn),去噪點(diǎn),取數(shù)據(jù)了,接下來這篇文章,補(bǔ)充一點(diǎn)相對(duì)高級(jí)的概念:1. 概念一:按單字符匹配 2. 概念二:匹配優(yōu)先和不匹配優(yōu)先 3. 概念三:貪婪模式與非貪婪模式 4. 概念四:環(huán)視(斷言) 5. 概念五:平衡組 6. 概念六:模式修飾符 7. 附:正則三段論應(yīng)用
概念一:按單字符匹配
正則里面的數(shù)據(jù)都是按照單個(gè)字符來進(jìn)行匹配的,這個(gè)通過數(shù)值區(qū)間的例子最容易體現(xiàn)出來,比如,示例一:
我要匹配0-15的數(shù)值區(qū)間,用正則來寫的話,便是[0-9]|1[0-5],這里,便是把0-9這種單字符的情況,和10-15這種多字符的情況拆分開了,使用分支|來區(qū)分開,表示要么是0-9,要么是10-15。
上面是兩位數(shù)值的情況,現(xiàn)在延伸至1-65535,我個(gè)人的處理思想是從大到小,一塊塊分解:
1. 65530-65535 ==> 6553[0-5] 末位區(qū)間0-5 2. 65500-65529 ==> 655[0-2][0-9] 第四位區(qū)間0-2,末位區(qū)間0-9 3. 65000-65499 ==> 65[0-4][0-9]{2} 第三位區(qū)間0-4,后兩位0-9 4. 60000-64999 ==> 6[0-4][0-9]{3} 第二位區(qū)間0-4,后三位0-9 5. 10000-59999 ==> [1-5][0-9]{4} 第一位區(qū)間1-5,后四位0-9 6. 1-9999 ==> [1-9][0-9]{0,3} 第一位只能是1-9,后三位可有可無
最后組合起來:
(6553[0-5]|655[0-2][0-9]|65[0-4][0-9]{2}|6[0-4][0-9]{3}|[1-5][0-9]{4}|[1-9][0-9]{0,3})
便得到1-65535匹配正則。
根據(jù)數(shù)據(jù)處理需求,可以在正則前后加上^和$,以匹配整個(gè)數(shù)據(jù)串,或者前后加入\b,把它當(dāng)做單詞邊界處理。沒有限定字符的邊界往往是js正則判斷中常見的錯(cuò)誤之一。
概念二:匹配優(yōu)先和不匹配優(yōu)先
匹配優(yōu)先和不匹配優(yōu)先從字面理解也是比較容易的,所謂匹配優(yōu)先,就是,能匹配我就先匹配;不匹配優(yōu)先就是,能不匹配我就先不匹配,這段匹配先跳過,先看看后面的匹配能不能通過。
概念三:貪婪模式與非貪婪模式
正則的貪婪模式和非貪婪模式是一個(gè)比較容易混淆的概念,不過,我們可以這么理解,一個(gè)人很貪婪,所以他會(huì)能拿多少拿多少,換過來,那就是貪婪模式下的正則表達(dá)式,能匹配多少就匹配多少,盡可能最多。而非貪婪模式,則是能不匹配就不匹配,盡可能最少。
下面舉個(gè)例子,示例二:
需求:匹配1后面跟任意個(gè)0 源串:10001 使用貪婪模式:10* 結(jié)果:1000 和 1 使用非貪婪模式:10*? 結(jié)果:1 和 1
首先,*是匹配0個(gè)或多個(gè)的意思。
貪婪模式下,它表示,首先匹配一個(gè)1,然后匹配1后面的0,最多可以匹配3個(gè)0,因此得到1000,然后第二次又匹配到一個(gè)1,但是后面沒有0,因此得到1;
非貪婪模式下,它表示,首先匹配一個(gè)1,然后1后面的0,能不匹配就不匹配了,所以,它每次都只是匹配了一個(gè)1。
看到這里,也許,有些人覺得,哎呀,我懂了!
那么,下來我們改改,看看你是不是真懂了。
示例三:
需求:匹配1后面跟任意個(gè)0,再跟一個(gè)1 源串:10001 使用貪婪模式:10*1 結(jié)果:10001 使用非貪婪模式:10*?1 結(jié)果:10001
為什么這兩次的結(jié)果一樣了?
因?yàn)椋齽t表達(dá)式要判斷完這整個(gè)正則才算成功,這種情況下,
貪婪模式,首先匹配一個(gè)1,然后匹配1后面盡可能多的0,發(fā)現(xiàn)3個(gè),再匹配0后面的一個(gè)1,正則表達(dá)式匹配完,完成匹配,得到10001;非貪婪模式,首先匹配一個(gè)1,然后,0*?是非貪婪模式,它不想匹配了(非貪婪模式不匹配優(yōu)先),看看后面是不是1了?然后發(fā)現(xiàn)哎媽呀,后面是個(gè)0啊,然后它回頭,不能再偷懶了,用0*?匹配一個(gè)0吧,然后匹配到10,又不想匹配了,看看后面有沒有1了,還是沒有,又回去用0*?匹配掉一個(gè)0,得到100,繼續(xù)偷懶,但是發(fā)現(xiàn)后面還不是1,然后又用0*?匹配得到1000,最后,發(fā)現(xiàn)真不容易啊,終于看到1了,匹配得到10001,正則表達(dá)式匹配完,完成匹配。
看到這里,是不是懂了?
那究竟哪個(gè)模式好呢?
什么時(shí)候使用貪婪模式,什么時(shí)候使用非貪婪模式,哪個(gè)性能好,哪個(gè)性能不好,不能一概而論,要根據(jù)情況分析。
下面我舉個(gè)例子:
源碼:
<a href="http://www.****.cn/my-regexp" rel="external nofollow" rel="external nofollow" target="_blank" title="我眼里的正則表達(dá)式(入門)">我眼里的正則表達(dá)式(入門)</a> <a title="我眼里的正則表達(dá)式(入門)" href="http://www.****.cn/my-regexp" rel="external nofollow" rel="external nofollow" target="_blank">我眼里的正則表達(dá)式(入門)</a>
正則1:<a [^>]*?href="([^"]*?)"[^>]*?>([^<]*?)</a>(238次)
正則2:<a [^>]*?href="([^"]*)"[^>]*>([^<]*)</a>(65次)
正則3:<a [^>]*href="([^"]*)"[^>]*>([^<]*)</a>(136次)
附:執(zhí)行次數(shù)的獲取請(qǐng)下載正則表達(dá)式測試工具:RegexBuddy 4.1.0-正則測試工具.rar,使用里面的Debug功能。
正則1是通用寫法,正則2是在確定字符不會(huì)溢出的情況下消除非貪婪模式,正則3是證明并不是全部消除非貪婪模式就是最優(yōu)。
因此,關(guān)于貪婪模式好還是非貪婪模式好的討論,只能說根據(jù)需求而定,不過,在平時(shí)的時(shí)候用,一般使用非貪婪模式較多,因?yàn)樨澙纺J浇?jīng)常會(huì)由于元字符范圍限制不嚴(yán)謹(jǐn)而導(dǎo)致匹配越界,得到非預(yù)期結(jié)果。
在確定的數(shù)據(jù)結(jié)構(gòu)里,可以嘗試使用[^>]*>這樣的排除字符貪婪模式替換非貪婪模式,提升匹配的效率。注意,貪婪部分([^>]*)的匹配,最好不要越過其后面的字符(>),否則會(huì)導(dǎo)致貪婪模式下的回溯,如正則3,[^>]*的匹配越過了href,一直匹配到>為止,而這時(shí)候再匹配href,會(huì)匹配不到而導(dǎo)致多次回溯處理,直到回溯到href前的位置,后面才繼續(xù)了下去。
另外,需要注意一點(diǎn),無論使用貪婪模式還是非貪婪模式,在不同語言需要注意回溯次數(shù)和嵌套次數(shù)的限制,比如在PHP中,pcre.backtrack_limit=100000,pcre.recursion_limit=100000。
概念四:環(huán)視(斷言/零寬斷言)
環(huán)視,在不同的地方又稱之為零寬斷言,簡稱斷言。
用一句通俗的話解釋:
環(huán)視,就是先從全局環(huán)顧一遍正則,(然后斷定結(jié)果,)再做進(jìn)一步匹配處理。
斷言,就是先從全局環(huán)顧一遍正則,然后斷定結(jié)果,再做進(jìn)一步匹配處理。
兩個(gè)雖然字面不一樣,意思卻是同一個(gè),都是做全局觀望,再做進(jìn)一步處理。
環(huán)視的作用相當(dāng)于對(duì)其所在位置加了一個(gè)附加條件,只有滿足這個(gè)條件,環(huán)視子表達(dá)式才能匹配成功。
環(huán)視主要有以下4個(gè)用法:
(?<=exp)匹配前面是exp的數(shù)據(jù)
(?<!exp)匹配前面不是exp的數(shù)據(jù)
(?=exp)匹配后面是exp的數(shù)據(jù)
(?!exp)匹配后面不是exp的數(shù)據(jù)
示例四:
(?<=B)AAA匹配前面是B的數(shù)據(jù),即BAAA匹配,而CAAA不匹配
(?<!B)AAA匹配前面不是B的數(shù)據(jù),即CAAA匹配,而BAAA不匹配
AAA(?=B)匹配后面是B的數(shù)據(jù),即AAAB匹配,而AAAC不匹配
AAA(?!B)匹配后面不是B的數(shù)據(jù),即AAAC能匹配,而AAAB不能匹配
另外,還會(huì)看到(?!B)[A-Z]這種寫法,其實(shí)它是[A-Z]范圍里,排除B的意思,前置的(?!B)只是對(duì)后面數(shù)據(jù)的一個(gè)限定,從而達(dá)到過濾匹配的效果。
因此,環(huán)視做排除處理是比較實(shí)用的,比如,示例五:
需求:字母、數(shù)字組合,不區(qū)分大小寫,不能純數(shù)字或者純字母,6-16個(gè)字符。 通用正則:^[a-z0-9]{6,16}$ 字母數(shù)字組合,6-16個(gè)字符 排除純字母:(?!^[a-z]+$) 排除純數(shù)字:(?!^[0-9]+$) 組合起來:(?!^[a-z]+$)(?!^[0-9]+$)^[a-z0-9]{6,16}$
注意,環(huán)視部分是不占寬度的,所以有零寬斷言的叫法。
所謂不占寬度,可以分成兩部分理解:
1、環(huán)視的匹配結(jié)果不納入數(shù)據(jù)結(jié)果
2、環(huán)視它匹配過的地方,下次還能用它繼續(xù)匹配。
如果不是環(huán)視,則匹配過的地方,不能再匹配第二次了。
上面示例四體現(xiàn)了:環(huán)視的匹配結(jié)果不納入數(shù)據(jù)結(jié)果,它的結(jié)果:
(?<=B)AAA 源串:BAAA 結(jié)果:AAA (?<!B)AAA 源串:CAAA 結(jié)果:AAA AAA(?=B) 源串:AAAB 結(jié)果:AAA AAA(?!B) 源串:AAAC 結(jié)果:AAA
而示例五體現(xiàn)了:環(huán)視它匹配過的地方,下次還能用它繼續(xù)匹配
因?yàn)椋麄€(gè)匹配過程中,正則表達(dá)式一共走了3次字符串匹配,第一次匹配不全部是字母,第二次匹配不全部是數(shù)字,第三次匹配全部是字母數(shù)字組合,6-16個(gè)字符。
擴(kuò)展部分: `[A-Z](?<=B)` [A-Z]范圍等于B `[A-Z](?<!B)` [A-Z]范圍排除B `(?!B)[A-Z]` [A-Z]范圍排除B
附: js不支持(?<=exp)和(?<!exp)語法
概念五:平衡組
平衡組并不是所有程序語言都支持,而我本人使用的PHP語言就不支持,所以平時(shí)接觸也是比較少的。
平衡組主要用到下面四個(gè)語法:
(?'group') 把捕獲的內(nèi)容命名為group,并壓入堆棧(Stack) (?'-group') 從堆棧上彈出最后壓入堆棧的名為group的捕獲內(nèi)容,如果堆棧本來為空,則本分組的匹配失敗 (?(group)yes|no) 如果堆棧上存在以名為group的捕獲內(nèi)容的話,繼續(xù)匹配yes部分的表達(dá)式,否則繼續(xù)匹配no部分 (?!) 零寬負(fù)向先行斷言,由于沒有后綴表達(dá)式,如沒有(?!B)的B,試圖匹配總是失敗
在PHP中是支持(?(group)yes|no)語法的,這里的group是分組編號(hào),即子模式編號(hào),如(A)?(?(1)yes|no) ,匹配Ayes 和 no
下面這里引用《正則表達(dá)式30分鐘入門教程#平衡組》關(guān)于<>配對(duì)匹配的例子,展示平衡組用法,
< #最外層的左括號(hào) [^<>]* #最外層的左括號(hào)后面的不是括號(hào)的內(nèi)容 ( ( (?'Open'<) #碰到了左括號(hào),在黑板上寫一個(gè)"Open" [^<>]* #匹配左括號(hào)后面的不是括號(hào)的內(nèi)容 )+ ( (?'-Open'>) #碰到了右括號(hào),擦掉一個(gè)"Open" [^<>]* #匹配右括號(hào)后面不是括號(hào)的內(nèi)容 )+ )* (?(Open)(?!)) #在遇到最外層的右括號(hào)時(shí),判斷黑板上還有沒有沒擦掉的"Open";如果還有,則匹配失敗 > #最外層的右括號(hào) 平衡組的一個(gè)最常見的應(yīng)用就是匹配HTML,下面這個(gè)例子可以匹配嵌套的<div>標(biāo)簽: <div[^>]*>[^<>]*(((?'Open'<div[^>]*>)[^<>]*)+((?'-Open'</div>)[^<>]*)+)*(?(Open)(?!))</div>
概念六:模式修飾符
模式修飾符在許多程序語言中都支持的,比如最常見的是i,不區(qū)分大小寫,如javascript里的/[a-z0-9]/i,表示匹配字母數(shù)字,不區(qū)分大小寫。
本人在寫php正則時(shí)常用的模式修飾符主要有i和s,如:
$pattern = '#[a-z0-9]+#is';
模式修飾符s的作用主要是的.能夠匹配換行,在處理換行數(shù)據(jù)時(shí),通常會(huì)用到。
在PHP中,模式修飾符有兩種用法,一種是上面的,在分隔符后面的模式修飾符,它的作用范圍是全局;另一種是在正則表達(dá)式中間的。
例如:
正則:/((?i)[A-Z]+)c/ 測試字符:abcABC 匹配:abc 說明:局部(ab)的大小寫被忽略了,(?i)的作用范圍在分組1內(nèi)
如果把正則改成:/([A-Z]+)c/i,則匹配結(jié)果將是:abcABC
概念七:正則三段論應(yīng)用
正則三段論:定錨點(diǎn),去噪點(diǎn),取數(shù)據(jù)
兩篇文章中,最重要的部分當(dāng)屬正則三段論:定錨點(diǎn),去噪點(diǎn),取數(shù)據(jù),它是整個(gè)正則處理過程中的靈魂,它貫穿整個(gè)正則撰寫過程。
下面舉例說明它的思想,示例六:
源數(shù)據(jù):標(biāo)題:深入正則表達(dá)式應(yīng)用,作者:Zjmainstay 需求:匹配作者名字
我要從源數(shù)據(jù)取到Zjmainstay這個(gè)作者名,那么,在這里,作者:就是我們所說的錨點(diǎn),因?yàn)樵谏厦孢@段數(shù)據(jù)中它能夠唯一定位到我們的數(shù)據(jù)Zjmainstay(就在它后面),因此,我們得到
(1) 定錨點(diǎn):作者:
而在這里,我們不需要關(guān)心標(biāo)題什么的,因此,標(biāo)題:深入正則表達(dá)式應(yīng)用,就是我們的噪點(diǎn),因此,我們得到
(2) 去噪點(diǎn)
最后,我們確定作者:后面就是我們的數(shù)據(jù),這個(gè)數(shù)據(jù)可以是任意字符,因此,我們得到正則:
作者:(.*)
而噪點(diǎn)部分,因?yàn)椴粫?huì)對(duì)數(shù)據(jù)取值造成干擾,直接去掉,不需要引入正則中。
下面再舉一個(gè)噪點(diǎn)干擾的例子,示例七:
源數(shù)據(jù): <a rel="external nofollow" rel="external nofollow" class="demo8" title="正則三段論應(yīng)用舉例">正則表達(dá)式入門教程</a> 需求:提取鏈接和標(biāo)題,還有a標(biāo)簽的文字
看到這個(gè)源數(shù)據(jù)和需求,我們必須定位好錨點(diǎn),主要有:
1.<a//必須是a標(biāo)簽
2.href=" 和 "//href=""的內(nèi)容得到鏈接
3.title=" 和 "//title=""的內(nèi)容得到標(biāo)題
4.> 和 </a>//>和</a>的內(nèi)容得到標(biāo)簽文字
然后,其他的都是噪點(diǎn),使用.*?替代,需要提取的數(shù)據(jù)部分使用括號(hào)獲取子模式,得到分組數(shù)據(jù),因此得到正則:
<a href="(.*?)".*?title="(.*?)">(.*?)</a>
看到這里,也許有朋友覺得,我還是不會(huì)寫,那么,再來一個(gè)更簡單的構(gòu)建方法,細(xì)化步驟,從源串逐步得到正則,示例八:
1. 直接拷貝源串,特殊字符處理轉(zhuǎn)義(本例沒特殊字符) <a rel="external nofollow" rel="external nofollow" class="demo8" title="正則三段論應(yīng)用舉例">正則表達(dá)式入門教程</a> 2. 從左到右,一段段轉(zhuǎn)化 2.1 <a href="(.*?)" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" class="demo8" title="正則三段論應(yīng)用舉例">正則表達(dá)式入門教程</a> 2.2 <a href="(.*?)" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" .*?title="正則三段論應(yīng)用舉例">正則表達(dá)式入門教程</a> 2.3 <a href="(.*?)" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" .*?title="(.*?)">正則表達(dá)式入門教程</a> 2.4 <a href="(.*?)" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" .*?title="(.*?)">(.*?)</a> 3. 得到最終的正則 <a href="(.*?)" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" .*?title="(.*?)">(.*?)</a>
至此,正則三段論的基本思想已經(jīng)展示完畢,大家還有什么不解請(qǐng)?jiān)u論留言,本人看到會(huì)第一時(shí)間給予回復(fù)。
熟悉正則三段論處理思想,剩下的便是基本語義的熟練程度了,這個(gè)通過多用多練可以達(dá)到熟能生巧的境界。
最后留下一句至尊提醒:.是萬能字符,大家看著用,遇到換行使用[\s\S]替換.即可。
到此這篇關(guān)于正則表達(dá)式7種高級(jí)應(yīng)用技巧教程的文章就介紹到這了,更多相關(guān)正則表達(dá)式高級(jí)教程內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
JavaScript email郵箱/郵件地址的正則表達(dá)式及分析
在做用戶注冊時(shí),常會(huì)用到郵箱/郵件地址的正則表達(dá)式。本文列舉了幾種方案,大家可以根據(jù)自己的項(xiàng)目情況,選擇最適合的方案2018-03-03Java 正則表達(dá)式匹配模式(貪婪型、勉強(qiáng)型、占有型)
這篇文章主要介紹了Java 正則表達(dá)式匹配模式(貪婪型、勉強(qiáng)型、占有型),需要的朋友可以參考下2016-12-12JS中的正則表達(dá)式及pattern的注意事項(xiàng)
本文給大家介紹js中的正則表達(dá)式的注意事項(xiàng)以及pattern的注意事項(xiàng),需要的小伙伴可以參考下本篇文章2015-10-10精通 JavaScript中的正則表達(dá)式手機(jī)整理 推薦
精通 JS正則表達(dá)式,想學(xué)習(xí)js正則表達(dá)式的朋友非常值得看,整理的比較不錯(cuò)。2009-10-10