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