PHP和正則表達(dá)式教程集合之二第2/2頁(yè)
更新時(shí)間:2007年03月19日 00:00:00 作者:
在Perl 5.6之前這的確有點(diǎn)困難,不過(guò)從Perl 5.6之后,引入了遞歸正則表達(dá)式,這個(gè)問題得到了解決。通常在正則表達(dá)式里用“(?R)”表示一個(gè)對(duì)自己的引用,下面讓我們看看用什么正則表達(dá)式來(lái)解決剛才提出的問題。
/( ( (?>[^()]+) | (?R) )* )/x
現(xiàn)在讓我們來(lái)分析這個(gè)模式的含義,這里使用了“x”模式修正符,以便可以在模式中加入空格以方便閱讀。
模式的開頭是匹配第一個(gè)左圓括號(hào),然后我們需要捕獲的子模式,注意,字模式后面跟了量詞“*”,表示此模式可以重復(fù)0到多次。最后是一個(gè)結(jié)束圓括號(hào)?,F(xiàn)在我們分析子模式( (?>[^()]+) | (?R) )的內(nèi)容。這是一個(gè)分支子模式,表示模式可以有兩種情況,第一種是(?>[^()]+),這是一個(gè)一次性子模式,代表一個(gè)以上的非括號(hào)字符,另一種情況是| (?R),也即對(duì)正則表達(dá)式自己的遞歸調(diào)用——( ( (?>[^()]+) | (?R) )* ),又尋找一個(gè)左圓括號(hào),開始查找一對(duì)嵌套的圓括號(hào)包含的內(nèi)容。
分析到這里,這個(gè)正則表達(dá)式的含義已經(jīng)基本清楚,但你注意到?jīng)]有,這里為什么要使用一次性子模式(?>[^()]+)來(lái)查找非括號(hào)字符串?
事實(shí)上,由于遞歸的層次是無(wú)限的,這種處理非常必要,特別是遇到不匹配的字符串時(shí),它不會(huì)讓你陷入長(zhǎng)時(shí)間的等待??紤]一下下面這個(gè)目標(biāo)字符串,
(aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa()
在得出不匹配的最終結(jié)果前,如果不使用一次性子模式,解析器將嘗試每一種可能的方法來(lái)分割目標(biāo)字符串,這將浪費(fèi)大量的時(shí)間。
用正則表達(dá)式修改目標(biāo)
并非所有的正則表達(dá)式工具都允許你修改目標(biāo)字符串,它們中的一些僅僅使用正則表達(dá)式來(lái)查找匹配指定模式的字符串,在Linux中,最為廣泛使用的支持正則表達(dá)式的工具就是grep命令,這是一個(gè)專門用來(lái)查找的工具,再就是一些文本編輯器工具,它們有的允許使用正則表達(dá)式替換,有的則不允許,這需要查看你使用的工具的在線手冊(cè)。
對(duì)于那些允許你使用正則表達(dá)式來(lái)修改目標(biāo)字符串的工具中,它們之間的一些不同你必然放在心上:
這些不同首先表現(xiàn)在替換的具體形式上,有的是以對(duì)話框的形式分別讓你輸入需要查找的模式和被替換的內(nèi)容,有些則使用命令使界面通過(guò)定界符來(lái)分隔匹配的模式與需要替換的內(nèi)容,對(duì)于一些編程語(yǔ)言工具,它們通常通過(guò)函數(shù)的不同參數(shù)來(lái)分別定義需要匹配的模式與替換的內(nèi)容。
另一個(gè)需要注意的不同是這些工具具體修改的對(duì)象。大多數(shù)基于Linux的命令行工具一般是通過(guò)標(biāo)準(zhǔn)輸出或者管道來(lái)修改緩存的內(nèi)容而非直接修改磁盤上存儲(chǔ)的文件,而文本編輯器工具或編程語(yǔ)言通常會(huì)直接修改目標(biāo)文件。
我們下面用Linux下sed命令的格式來(lái)舉幾個(gè)正則表達(dá)式的例子:
模式:s/cat/dog/g
輸入:wild dogs, bobcats, lions, and other wild cats
輸出:wild dogs, bobdogs, lions, and other wild dogs
模式:s/[a-z]+i[a-z]*/nice/g
輸入:wild dogs, bobcats, lions, and other wild cats
輸出: nice dogs, bobcats, nice, and other nice cats
當(dāng)我們使用模式進(jìn)行替換操作時(shí),目標(biāo)字符串中所有匹配模式的字符串都將被替換。
下面再舉一個(gè)使用逆向引用進(jìn)行替換的例子:
模式:s/([A-Z])([0-9]{2,4}) /2:1 /g
輸入: A37 B4 C107 D54112 E1103 XXX
輸出: 37:A B4 107:C D54112 1103:E XXX
前面已經(jīng)介紹過(guò)默認(rèn)情況下的匹配一般是greedy的,這常會(huì)使實(shí)際匹配的部分大于你希望匹配的內(nèi)容,特別是在進(jìn)行替換操作時(shí)這將更加危險(xiǎn),因?yàn)槿绻阍阱e(cuò)誤匹配的情況下執(zhí)行了一次替換操作,實(shí)際上你是刪除了目標(biāo)中的有效內(nèi)容,特別是當(dāng)這種操作面向文件時(shí)造成的危害就更大了。因此,牢記一個(gè)不嚴(yán)格的字符類加上一個(gè)不嚴(yán)格的量詞足以造成不可挽回的后果,執(zhí)行類似操作前一定要多測(cè)試一下不同的目標(biāo)字符串,盡可能避免這種情況的發(fā)生。
在本教程的下一篇文章里,我們會(huì)介紹一款可以方便進(jìn)行正則表達(dá)式學(xué)習(xí)的工具和一些正則表達(dá)式編寫的思路。
正則表達(dá)式快速入門(五)
在上一篇文章里,我們介紹了正則表達(dá)式中的遞歸與替換,現(xiàn)在讓我們接觸一個(gè)學(xué)習(xí)正則表達(dá)式時(shí)方便測(cè)試使用的工具,并介紹一些正則表達(dá)式的編寫思路。
一個(gè)學(xué)習(xí)正則表達(dá)式的便捷工具
學(xué)習(xí)正則表達(dá)式最好的方法當(dāng)然是實(shí)踐,不過(guò)支持正則表達(dá)式的工具雖多,但如果僅僅用來(lái)做練習(xí)卻不是很方便。
這里我向一家推薦一款專門的正則表達(dá)式編寫測(cè)試工具,PHPEdit公司的Regular Expression Editor工具。這是一個(gè)免費(fèi)軟件,主要用來(lái)調(diào)試PHP使用的Perl兼容正則表達(dá)式函數(shù)。使用它可以方便的輸入目標(biāo)字符串和正則表達(dá)式,并實(shí)時(shí)看到匹配的結(jié)果??梢缘剿南螺d網(wǎng)頁(yè)去下載這個(gè)工具。
程序的界面非常簡(jiǎn)明,不過(guò)使用中發(fā)現(xiàn),它的一些功能使用起來(lái)好像有問題,只有preg_match_all和preg_replace功能正常,另外在匹配模式輸入框中,不要加模式定界符,程序好像把該輸入框中的全部?jī)?nèi)容都作為模式來(lái)解析。
好在做為一個(gè)正則表達(dá)式的練習(xí)工具,它的功能是足夠了,下面是它的運(yùn)行界面。
程序運(yùn)行界面
文中提到的各個(gè)例子都可以在里面進(jìn)行測(cè)試,在最上面的框里輸入模式,把目標(biāo)字符串寫進(jìn)中間的輸入框,點(diǎn)擊“run the regxwp”按鈕可以在下面得到匹配結(jié)果。
正則表達(dá)式的編寫思路
一個(gè)避免過(guò)多匹配的小技巧
前面我們已經(jīng)多此談到書寫不合理的正則表達(dá)式引起過(guò)多匹配的問題,現(xiàn)在的問題是,如何可以盡量避免類似的情況發(fā)生。這里有個(gè)小小的技巧。
如果你發(fā)現(xiàn)你定制模式匹配了過(guò)多的結(jié)果,一個(gè)好的方法是換個(gè)思路,與其考慮我的模式下一步需要匹配什么,不如考慮我的模式下一步需要避免匹配什么。我們可以用元字答“^”和字符類很容易的達(dá)成這種效果,這常??梢缘玫礁_的匹配。
為了說(shuō)明這種思路的好處我們先來(lái)舉一個(gè)與正則表達(dá)式無(wú)關(guān)的例子,考慮這樣一個(gè)問題,你把一個(gè)骰子一次拋出6的概率是六分之一,如果讓你擲六次,擲出一個(gè)6的概率是多少呢?
可能有人會(huì)這么算,一次的概率是1/6,六次是就是6個(gè)1/6,加起來(lái)等于1。這個(gè)結(jié)果明顯是錯(cuò)的,雖然你擲了六次,但肯定不能保證必然會(huì)擲出一個(gè)6。從正向的思路解這道題看上去有點(diǎn)難。
如果我們換個(gè)思路,解決的方法就明確多了。我們可以把這個(gè)題的問法改成這樣,如果讓你擲6次骰子,每一次都擲不出6的概率是多少?這個(gè)問題就好解多了,根據(jù)概率的乘法原理,每一次擲出不是6的點(diǎn)數(shù)的概率是5/6,而6次中每一次都不是6的概率是5/6的6次方,大概等于33%的樣子,然后用1減去這個(gè)數(shù)字就可以得到我們需要的答案。
你可以把模式中每部分的匹配看作擲一次骰子的過(guò)程,每一部分的匹配概率與總匹配概率的情況與我們上面這個(gè)例子非常相似。
如何提高正則表達(dá)式的解析效率
對(duì)同樣匹配內(nèi)容的正則表達(dá)式而言,一些模式往往比另外一些模式更有效率。舉一個(gè)簡(jiǎn)單的例子,使用字符類“[aeiou]”會(huì)比使用分支選擇型模式“(a|e|i|o|u).”更有效,一般而言,使用盡可能簡(jiǎn)單和基本的模式通過(guò)會(huì)得到更高的效率。
應(yīng)該盡可能的慎用相互嵌套的無(wú)限重復(fù)量詞,當(dāng)遇到不匹配的目標(biāo)字符串時(shí),對(duì)字符串的解析有可能花掉很可觀的時(shí)間。比如下面這個(gè)模式片斷“(a+)*”,當(dāng)遇到不匹配的目標(biāo)字符串“aaaa”時(shí),解析器會(huì)對(duì)它嘗試33種不同的匹配方法,這個(gè)數(shù)目會(huì)隨不匹配字符串長(zhǎng)度的增加而極快的增長(zhǎng)。
一些正則表達(dá)式工具對(duì)一些特定的模式匹配進(jìn)行了優(yōu)化以提高效率,了解你使用的正則表達(dá)式工作做過(guò)些什么優(yōu)化并盡可能利用經(jīng)過(guò)優(yōu)化的模式可以大大提高你的正則表達(dá)式執(zhí)行效率。例如,PHP對(duì)形如/a+)*b /這樣的模式的解析進(jìn)行了優(yōu)化,當(dāng)模式結(jié)尾是一個(gè)確定的字符時(shí),解析器會(huì)先查找目標(biāo)的結(jié)尾是否符合模式,如果否則立刻返回失敗的匹配結(jié)果并停止解析。如果將上面的樣式改為“(a+)*d”時(shí),因?yàn)榻Y(jié)尾不再是一個(gè)確定的字符,此模式會(huì)按正常的過(guò)程解析。如果你想看一下兩者效果的差異,你在我們前面提到的工具中,把目標(biāo)字符串設(shè)置成25個(gè)小寫的a字符,然后分別測(cè)試兩個(gè)模式,前者立刻就結(jié)束了,而后者需要等待約一秒(筆者使用的是XP1700+處理器)。
除了盡可能利用經(jīng)過(guò)優(yōu)化的模式,對(duì)一些模式進(jìn)行重新構(gòu)造也可以大大提高效率。我們?cè)诮榻B后向斷言時(shí)介紹過(guò)的那個(gè)利用后向斷言結(jié)合一次性子模式匹配結(jié)尾的字符的方法就是一個(gè)很好的例子。
這里我們準(zhǔn)備結(jié)束這個(gè)教程,由于篇幅和本人水平的限制文中可能會(huì)有很多疏漏,還要請(qǐng)求大家諒解。對(duì)正則表達(dá)式介紹最全面的可能還是Perl相關(guān)的一些文檔和著作,如果想對(duì)正則表達(dá)式進(jìn)行更深入的了解可以參看Jeffrey Friedl 寫的“Mastering Regular Expressions”一書,里面有很多例子。不過(guò)我覺得在了解正則表達(dá)式基本概念后,還是仔細(xì)讀一下自己經(jīng)常使用的相關(guān)工具里的正則表達(dá)式相關(guān)部分更實(shí)用一些,最后,還是那句話,實(shí)踐出真知,希望大家在不斷實(shí)踐中更好的掌握正則表達(dá)式的使用。
正則表達(dá)式中的特殊字符
轉(zhuǎn)自:http://www.phpe.net/articles/151.shtml
字符 描述
將下一個(gè)字符標(biāo)記為一個(gè)特殊字符、或一個(gè)原義字符、或一個(gè)后向引用、或一個(gè)八進(jìn)制轉(zhuǎn)義符。例如,'n' 匹配字符 "n"。'n' 匹配一個(gè)換行符。序列 '' 匹配 "" 而 "(" 則匹配 "("。
^
匹配輸入字符串的開始位置。如果設(shè)置了 RegExp 對(duì)象的 Multiline 屬性,^ 也匹配 'n' 或 'r' 之后的位置。
$
匹配輸入字符串的結(jié)束位置。如果設(shè)置了RegExp 對(duì)象的 Multiline 屬性,$ 也匹配 'n' 或 'r' 之前的位置。
*
匹配前面的子表達(dá)式零次或多次。例如,zo* 能匹配 "z" 以及 "zoo"。 * 等價(jià)于{0,}。
+ 匹配前面的子表達(dá)式一次或多次。例如,'zo+' 能匹配 "zo" 以及 "zoo",但不能匹配 "z"。+ 等價(jià)于 {1,}。
?
匹配前面的子表達(dá)式零次或一次。例如,"do(es)?" 可以匹配 "do" 或 "does" 中的"do" 。? 等價(jià)于 {0,1}。
{n}
n 是一個(gè)非負(fù)整數(shù)。匹配確定的 n 次。例如,'o{2}' 不能匹配 "Bob" 中的 'o',但是能匹配 "food" 中的兩個(gè) o。
{n,}
n 是一個(gè)非負(fù)整數(shù)。至少匹配n 次。例如,'o{2,}' 不能匹配 "Bob" 中的 'o',但能匹配 "foooood" 中的所有 o。'o{1,}' 等價(jià)于 'o+'。'o{0,}' 則等價(jià)于 'o*'。
{n,m}
m 和 n 均為非負(fù)整數(shù),其中n <= m。最少匹配 n 次且最多匹配 m 次。劉, "o{1,3}" 將匹配 "fooooood" 中的前三個(gè) o。'o{0,1}' 等價(jià)于 'o?'。請(qǐng)注意在逗號(hào)和兩個(gè)數(shù)之間不能有空格。
?
當(dāng)該字符緊跟在任何一個(gè)其他限制符 (*, +, ?, {n}, {n,}, {n,m}) 后面時(shí),匹配模式是非貪婪的。非貪婪模式盡可能少的匹配所搜索的字符串,而默認(rèn)的貪婪模式則盡可能多的匹配所搜索的字符串。例如,對(duì)于字符串 "oooo",'o+?' 將匹配單個(gè) "o",而 'o+' 將匹配所有 'o'。
.
匹配除 "n" 之外的任何單個(gè)字符。要匹配包括 'n' 在內(nèi)的任何字符,請(qǐng)使用象 '[.n]' 的模式。
(pattern)
匹配pattern 并獲取這一匹配。所獲取的匹配可以從產(chǎn)生的 Matches 集合得到,在VBScript 中使用 SubMatches 集合,在JScript 中則使用 {CONTENT}… 屬性。要匹配圓括號(hào)字符,請(qǐng)使用 '(' 或 ')'。
(?:pattern)
匹配 pattern 但不獲取匹配結(jié)果,也就是說(shuō)這是一個(gè)非獲取匹配,不進(jìn)行存儲(chǔ)供以后使用。這在使用 "或" 字符 (|) 來(lái)組合一個(gè)模式的各個(gè)部分是很有用。例如, 'industr(?:y|ies) 就是一個(gè)比 'industry|industries' 更簡(jiǎn)略的表達(dá)式。
(?=pattern)
正向預(yù)查,在任何匹配 pattern 的字符串開始處匹配查找字符串。這是一個(gè)非獲取匹配,也就是說(shuō),該匹配不需要獲取供以后使用。例如, 'Windows (?=95|98|NT|2000)' 能匹配 "Windows 2000" 中的 "Windows" ,但不能匹配 "Windows 3.1" 中的 "Windows"。預(yù)查不消耗字符,也就是說(shuō),在一個(gè)匹配發(fā)生后,在最后一次匹配之后立即開始下一次匹配的搜索,而不是從包含預(yù)查的字符之后開始。
(?!pattern)
負(fù)向預(yù)查,在任何不匹配Negative lookahead matches the search string at any point where a string not matching pattern 的字符串開始處匹配查找字符串。這是一個(gè)非獲取匹配,也就是說(shuō),該匹配不需要獲取供以后使用。例如'Windows (?!95|98|NT|2000)' 能匹配 "Windows 3.1" 中的 "Windows",但不能匹配 "Windows 2000" 中的 "Windows"。預(yù)查不消耗字符,也就是說(shuō),在一個(gè)匹配發(fā)生后,在最后一次匹配之后立即開始下一次匹配的搜索,而不是從包含預(yù)查的字符之后開始
x|y
匹配 x 或 y。例如,'z|food' 能匹配 "z" 或 "food"。'(z|f)ood' 則匹配 "zood" 或 "food"。
[xyz]
字符集合。匹配所包含的任意一個(gè)字符。例如, '[abc]' 可以匹配 "plain" 中的 'a'。
[^xyz]
負(fù)值字符集合。匹配未包含的任意字符。例如, '[^abc]' 可以匹配 "plain" 中的'p'。
[a-z]
字符范圍。匹配指定范圍內(nèi)的任意字符。例如,'[a-z]' 可以匹配 'a' 到 'z' 范圍內(nèi)的任意小寫字母字符。
[^a-z]
負(fù)值字符范圍。匹配任何不在指定范圍內(nèi)的任意字符。例如,'[^a-z]' 可以匹配任何不在 'a' 到 'z' 范圍內(nèi)的任意字符。
匹配一個(gè)單詞邊界,也就是指單詞和空格間的位置。例如, 'erb' 可以匹配"never" 中的 'er',但不能匹配 "verb" 中的 'er'。
匹配非單詞邊界。'erB' 能匹配 "verb" 中的 'er',但不能匹配 "never" 中的 'er'。
cx
匹配由x指明的控制字符。例如, cM 匹配一個(gè) Control-M 或回車符。 x 的值必須為 A-Z 或 a-z 之一。否則,將 c 視為一個(gè)原義的 'c' 字符。
d
匹配一個(gè)數(shù)字字符。等價(jià)于 [0-9]。
D
匹配一個(gè)非數(shù)字字符。等價(jià)于 [^0-9]。
f
匹配一個(gè)換頁(yè)符。等價(jià)于 x0c 和 cL。
匹配一個(gè)換行符。等價(jià)于 x0a 和 cJ。
r
匹配一個(gè)回車符。等價(jià)于 x0d 和 cM。
匹配任何空白字符,包括空格、制表符、換頁(yè)符等等。等價(jià)于 [ fnrtv]。
匹配任何非空白字符。等價(jià)于 [^ fnrtv]。
t
匹配一個(gè)制表符。等價(jià)于 x09 和 cI。
v
匹配一個(gè)垂直制表符。等價(jià)于 x0b 和 cK。
w
匹配包括下劃線的任何單詞字符。等價(jià)于'[A-Za-z0-9_]'。
W
匹配任何非單詞字符。等價(jià)于 '[^A-Za-z0-9_]'。
xn
匹配 n,其中 n 為十六進(jìn)制轉(zhuǎn)義值。十六進(jìn)制轉(zhuǎn)義值必須為確定的兩個(gè)數(shù)字長(zhǎng)。例如, 'x41' 匹配 "A"。'x041' 則等價(jià)于 'x04' & "1"。正則表達(dá)式中可以使用 ASCII 編碼。.
num
匹配 num,其中 num 是一個(gè)正整數(shù)。對(duì)所獲取的匹配的引用。例如,'(.)' 匹配兩個(gè)連續(xù)的相同字符。
標(biāo)識(shí)一個(gè)八進(jìn)制轉(zhuǎn)義值或一個(gè)后向引用。如果 n 之前至少 n 個(gè)獲取的子表達(dá)式,則 n 為后向引用。否則,如果 n 為八進(jìn)制數(shù)字 (0-7),則 n 為一個(gè)八進(jìn)制轉(zhuǎn)義值。
nm
標(biāo)識(shí)一個(gè)八進(jìn)制轉(zhuǎn)義值或一個(gè)后向引用。如果 nm 之前至少有is preceded by at least nm 個(gè)獲取得子表達(dá)式,則 nm 為后向引用。如果 nm 之前至少有 n 個(gè)獲取,則 n 為一個(gè)后跟文字 m 的后向引用。如果前面的條件都不滿足,若 n 和 m 均為八進(jìn)制數(shù)字 (0-7),則 nm 將匹配八進(jìn)制轉(zhuǎn)義值 nm。
nml
如果 n 為八進(jìn)制數(shù)字 (0-3),且 m 和 l 均為八進(jìn)制數(shù)字 (0-7),則匹配八進(jìn)制轉(zhuǎn)義值 nml。
un
匹配 n,其中 n 是一個(gè)用四個(gè)十六進(jìn)制數(shù)字表示的 Unicode 字符。例如,u00A9 匹配版權(quán)符號(hào) (?)。
相關(guān)文章
JavaScript 正則表達(dá)式驗(yàn)證函數(shù)代碼
上篇文章《JavaScript驗(yàn)證正則表達(dá)式大全》說(shuō)的是javascript中使用的正則表達(dá)式的例子,但是沒有說(shuō)這些正則表達(dá)式如何使用,現(xiàn)在給大家?guī)讉€(gè)例子,大家可以看看。2010-05-05JavaScript正則表達(dá)式校驗(yàn)非零的負(fù)整數(shù)實(shí)例
本文分享了JavaScript正則表達(dá)式(^-[1-9]\d*$)校驗(yàn)非零的負(fù)整數(shù)實(shí)例代碼,代碼簡(jiǎn)單易懂,需要的朋友可以看下2016-12-12正則表達(dá)式語(yǔ)法規(guī)則及在Javascript和C#中的使用方法
正則表達(dá)式通常被用來(lái)檢索和/或替換那些符合某個(gè)模式的文本內(nèi)容。許多程序設(shè)計(jì)語(yǔ)言都支持利用正則表達(dá)式進(jìn)行字符串操作2013-10-10JavaScript中的正則表達(dá)式使用及驗(yàn)證qq號(hào)碼的正則
這篇文章給大家介紹了javascript中的正則表達(dá)式使用及驗(yàn)證qq號(hào)碼的正則表達(dá)式,涉及到手機(jī)號(hào)、姓名、qq相關(guān)知識(shí)驗(yàn)證,感興趣的朋友一起學(xué)習(xí)吧2015-11-11正則表達(dá)式 特殊字符應(yīng)用分析[簡(jiǎn)單詳細(xì)入門必看]
網(wǎng)上的東西,都沒有這個(gè)詳細(xì),對(duì)于想入手正則表達(dá)式高級(jí)應(yīng)用的朋友,也是個(gè)不錯(cuò)的資料參考。2008-11-11