PHP中的遞歸正則使用說(shuō)明
更新時(shí)間:2010年07月27日 21:10:57 作者:
本文內(nèi)容, 整理自網(wǎng)文Finer points of PHP regular expressions. 其分析過(guò)程剝繭抽絲, 絲絲入扣, 值得一讀. 該文系統(tǒng)地列出了PHP中正則表達(dá)式常見(jiàn)特性, 我只摘取其中遞歸部分翻譯整理出來(lái).
之前一篇文章翻譯了Perl語(yǔ)言中的遞歸正則表達(dá)式. 其實(shí)不少語(yǔ)言中的正則都是支持遞歸的, 例如本文要介紹的PHP正則遞歸. 雖然, 工作中最常用的正則表達(dá)式都很”正則”, 只用最基本的語(yǔ)法就能解決85%以上的問(wèn)題, 而且合理有效地使用普通正則來(lái)解決復(fù)雜問(wèn)題也是一門(mén)技巧與學(xué)問(wèn); 但是高級(jí)一點(diǎn)的語(yǔ)法的確有它存的價(jià)值, 有時(shí)不用它還真辦不了事兒; 況且學(xué)習(xí)正則的樂(lè)趣也在于嘗試各種各樣的可能性, 滿(mǎn)足自己無(wú)窮無(wú)盡的好奇心.
本文內(nèi)容, 整理自網(wǎng)文Finer points of PHP regular expressions. 其分析過(guò)程剝繭抽絲, 絲絲入扣, 值得一讀. 該文系統(tǒng)地列出了PHP中正則表達(dá)式常見(jiàn)特性, 我只摘取其中遞歸部分翻譯整理出來(lái).
正文
例子
什么時(shí)候會(huì)用到遞歸正則表達(dá)式呢? 當(dāng)然是待匹配的字串中遞歸地出現(xiàn)某種模式時(shí)(貌似廢話). 最經(jīng)典的例子, 就是遞歸正則處理嵌套括號(hào)的問(wèn)題了. 例子如下.
假設(shè)你的文本中包含了正確配對(duì)的嵌套括號(hào). 括號(hào)的深度可以是無(wú)限層. 你想捕獲這樣的括號(hào)組.
恕我劇透, 標(biāo)準(zhǔn)答案是這樣的:
<?php
$string = "some text (a(b(c)d)e) more text";
if(preg_match("/\(([^()]+|(?R))*\)/",$string,$matches))
{
echo "<pre>"; print_r($matches); echo "</pre>";
}
?>
其輸出結(jié)果是:
Array
(
[0] => (a(b(c)d)e)
[1] => e
)
可見(jiàn), 我們所需要的文本, 已經(jīng)捕獲到$matches[0]中了.
原理
現(xiàn)在思考原理.
上面的正則表達(dá)式中的關(guān)鍵點(diǎn)是(?R). (?R)的作用就是遞歸地替換它所在的整條正則表達(dá)式. 在每次迭代時(shí), PHP 語(yǔ)法分析器都會(huì)將(?R)替換為”\(([^()]+|(?R))*\)“.
因此, 具體到上述的例子, 其正則表達(dá)式等價(jià)于:
"/\(([^()]+|\(([^()]+|\(([^()]+)*\))*\))*\)/"
但是上面的代碼只適合深度為3層的括號(hào). 對(duì)于未知深度的括號(hào)嵌套, 就只好使用這種正則了:
"/\(([^()]+|(?R))*\)/"
它不但能夠匹配無(wú)限深度, 還簡(jiǎn)化了正則表達(dá)式的語(yǔ)法. 功能強(qiáng)大, 語(yǔ)法簡(jiǎn)潔.
現(xiàn)在來(lái)細(xì)看一下"/\(([^()]+|(?R))*\)/"是怎樣匹配"(a(b(c)d)e)"的:
"(c)"這部分被正則式 "\(([^()]+)*\)" 匹配. 請(qǐng)注意, (c) 其實(shí)就相當(dāng)于整個(gè)遞歸的一個(gè)縮影, 麻雀雖小五臟俱全, 因此它用到了整個(gè)正則表達(dá)式.
換言之, 下一步中的(c), 可以使用(?R) 來(lái)匹配.
(b(c)d)的匹配過(guò)程為:
"\("匹配"(";
"[^()]+"匹配"b";
(?R)匹配"(c)";
"[^()]+"匹配"d";
"\)"匹配")".
根據(jù)上面的匹配原理, 不難理解為什么數(shù)組的第2個(gè)元素$matches[1]與'e'等價(jià). 子串'e'是在最后一次匹配迭代中被捕獲. 匹配過(guò)程中, 只有最后一次的捕獲結(jié)果才會(huì)保存到數(shù)組中.
rex注: 關(guān)于這個(gè)特性, 可以自行嘗試一下, 看看使用正則式([a-z]+[0-9]+)+來(lái)匹配字串a(chǎn)bc123xyz890, 其捕獲結(jié)果$1是什么. 注意, 其結(jié)果與 Left Longest 原理并不沖突.
如果我們只需要捕獲 $matches[0], 可以這樣做:
<?php
$string = "some text (a(b(c)d)e) more text";
if(preg_match("/\((?:[^()]+|(?R))*\)/",$string,$matches))
{
echo "<pre>"; print_r($matches); echo "</pre>";
}
?>
產(chǎn)生的結(jié)果相同:
Array
(
[0] => (a(b(c)d)e)
)
所做的改動(dòng)是捕獲括號(hào)()改為非捕獲捕獲括號(hào)(?:)了.
還可以進(jìn)一步完善為:
<?php
$string = "some text (a(b(c)d)e) more text";
if(preg_match("/\((?>[^()]+|(?R))*\)/",$string,$matches))
{
echo "<pre>"; print_r($matches); echo "</pre>";
}
?>
這里我們用到了所謂的一次性模式(rex注: 余晟先生譯的《精通正則表達(dá)式v3.0》中, 謂之”固化分組”. 可參考該書(shū).) PHP手冊(cè)也推薦只要條件允許, 就盡可能使用這種模式, 以便提升正則表達(dá)式的速度.
一次性模式很簡(jiǎn)單, 這里不再詳述. 如果感興趣, 可以參考PHP 官方手冊(cè). 如果您想深入學(xué)習(xí)PERL兼容式正則表達(dá)式, 請(qǐng)參考文末鏈接.
原文: Finer points of PHP regular expressions
Perl兼容正則表達(dá)式 官網(wǎng) 文檔
PHP官網(wǎng)的PCRE正則文檔
本文內(nèi)容, 整理自網(wǎng)文Finer points of PHP regular expressions. 其分析過(guò)程剝繭抽絲, 絲絲入扣, 值得一讀. 該文系統(tǒng)地列出了PHP中正則表達(dá)式常見(jiàn)特性, 我只摘取其中遞歸部分翻譯整理出來(lái).
正文
例子
什么時(shí)候會(huì)用到遞歸正則表達(dá)式呢? 當(dāng)然是待匹配的字串中遞歸地出現(xiàn)某種模式時(shí)(貌似廢話). 最經(jīng)典的例子, 就是遞歸正則處理嵌套括號(hào)的問(wèn)題了. 例子如下.
假設(shè)你的文本中包含了正確配對(duì)的嵌套括號(hào). 括號(hào)的深度可以是無(wú)限層. 你想捕獲這樣的括號(hào)組.
恕我劇透, 標(biāo)準(zhǔn)答案是這樣的:
復(fù)制代碼 代碼如下:
<?php
$string = "some text (a(b(c)d)e) more text";
if(preg_match("/\(([^()]+|(?R))*\)/",$string,$matches))
{
echo "<pre>"; print_r($matches); echo "</pre>";
}
?>
其輸出結(jié)果是:
復(fù)制代碼 代碼如下:
Array
(
[0] => (a(b(c)d)e)
[1] => e
)
可見(jiàn), 我們所需要的文本, 已經(jīng)捕獲到$matches[0]中了.
原理
現(xiàn)在思考原理.
上面的正則表達(dá)式中的關(guān)鍵點(diǎn)是(?R). (?R)的作用就是遞歸地替換它所在的整條正則表達(dá)式. 在每次迭代時(shí), PHP 語(yǔ)法分析器都會(huì)將(?R)替換為”\(([^()]+|(?R))*\)“.
因此, 具體到上述的例子, 其正則表達(dá)式等價(jià)于:
"/\(([^()]+|\(([^()]+|\(([^()]+)*\))*\))*\)/"
但是上面的代碼只適合深度為3層的括號(hào). 對(duì)于未知深度的括號(hào)嵌套, 就只好使用這種正則了:
"/\(([^()]+|(?R))*\)/"
它不但能夠匹配無(wú)限深度, 還簡(jiǎn)化了正則表達(dá)式的語(yǔ)法. 功能強(qiáng)大, 語(yǔ)法簡(jiǎn)潔.
現(xiàn)在來(lái)細(xì)看一下"/\(([^()]+|(?R))*\)/"是怎樣匹配"(a(b(c)d)e)"的:
"(c)"這部分被正則式 "\(([^()]+)*\)" 匹配. 請(qǐng)注意, (c) 其實(shí)就相當(dāng)于整個(gè)遞歸的一個(gè)縮影, 麻雀雖小五臟俱全, 因此它用到了整個(gè)正則表達(dá)式.
換言之, 下一步中的(c), 可以使用(?R) 來(lái)匹配.
(b(c)d)的匹配過(guò)程為:
"\("匹配"(";
"[^()]+"匹配"b";
(?R)匹配"(c)";
"[^()]+"匹配"d";
"\)"匹配")".
根據(jù)上面的匹配原理, 不難理解為什么數(shù)組的第2個(gè)元素$matches[1]與'e'等價(jià). 子串'e'是在最后一次匹配迭代中被捕獲. 匹配過(guò)程中, 只有最后一次的捕獲結(jié)果才會(huì)保存到數(shù)組中.
rex注: 關(guān)于這個(gè)特性, 可以自行嘗試一下, 看看使用正則式([a-z]+[0-9]+)+來(lái)匹配字串a(chǎn)bc123xyz890, 其捕獲結(jié)果$1是什么. 注意, 其結(jié)果與 Left Longest 原理并不沖突.
如果我們只需要捕獲 $matches[0], 可以這樣做:
復(fù)制代碼 代碼如下:
<?php
$string = "some text (a(b(c)d)e) more text";
if(preg_match("/\((?:[^()]+|(?R))*\)/",$string,$matches))
{
echo "<pre>"; print_r($matches); echo "</pre>";
}
?>
產(chǎn)生的結(jié)果相同:
復(fù)制代碼 代碼如下:
Array
(
[0] => (a(b(c)d)e)
)
所做的改動(dòng)是捕獲括號(hào)()改為非捕獲捕獲括號(hào)(?:)了.
還可以進(jìn)一步完善為:
復(fù)制代碼 代碼如下:
<?php
$string = "some text (a(b(c)d)e) more text";
if(preg_match("/\((?>[^()]+|(?R))*\)/",$string,$matches))
{
echo "<pre>"; print_r($matches); echo "</pre>";
}
?>
這里我們用到了所謂的一次性模式(rex注: 余晟先生譯的《精通正則表達(dá)式v3.0》中, 謂之”固化分組”. 可參考該書(shū).) PHP手冊(cè)也推薦只要條件允許, 就盡可能使用這種模式, 以便提升正則表達(dá)式的速度.
一次性模式很簡(jiǎn)單, 這里不再詳述. 如果感興趣, 可以參考PHP 官方手冊(cè). 如果您想深入學(xué)習(xí)PERL兼容式正則表達(dá)式, 請(qǐng)參考文末鏈接.
您可能感興趣的文章:
- 使用PHP數(shù)組實(shí)現(xiàn)無(wú)限分類(lèi),不使用數(shù)據(jù)庫(kù),不使用遞歸.
- php遞歸實(shí)現(xiàn)無(wú)限分類(lèi)生成下拉列表的函數(shù)
- php實(shí)現(xiàn)無(wú)限級(jí)分類(lèi)實(shí)現(xiàn)代碼(遞歸方法)
- PHP 無(wú)限分類(lèi)三種方式 非函數(shù)的遞歸調(diào)用!
- 淺析PHP遞歸函數(shù)返回值使用方法
- PHP遞歸算法的詳細(xì)示例分析
- php遞歸使用示例(php遞歸函數(shù))
- php function用法如何遞歸及return和echo區(qū)別
- php+mysql不用遞歸實(shí)現(xiàn)的無(wú)限級(jí)分類(lèi)實(shí)例(非遞歸)
- php無(wú)限極分類(lèi)遞歸排序?qū)崿F(xiàn)方法
- php使用遞歸計(jì)算文件夾大小
相關(guān)文章
正則表達(dá)式 學(xué)習(xí)參考 推薦入門(mén)者看
正則表達(dá)式(Regular Expression)是一種匹配模式,描述的是一串文本的特征。他可以實(shí)現(xiàn)一些可變字符的替換。2009-07-07
Java用正則表達(dá)式如何讀取網(wǎng)頁(yè)內(nèi)容
java用正則表達(dá)式讀取網(wǎng)頁(yè)內(nèi)容,通過(guò)抓取文章標(biāo)題及內(nèi)容,進(jìn)一步專(zhuān)區(qū)整個(gè)頁(yè)面的全部?jī)?nèi)容,本文代碼簡(jiǎn)單易懂,對(duì)java用正則表達(dá)式讀取網(wǎng)頁(yè)內(nèi)容感興趣的朋友可以參考下2015-10-10
正則表達(dá)式之零寬斷言實(shí)例詳解【基于PHP】
這篇文章主要介紹了正則表達(dá)式之零寬斷言,簡(jiǎn)單介紹了零寬斷言的概念、分類(lèi)及php實(shí)現(xiàn)技巧與相關(guān)注意事項(xiàng),需要的朋友可以參考下2017-04-04
一個(gè)驗(yàn)證用戶(hù)名的正則表達(dá)式
一個(gè)驗(yàn)證用戶(hù)名的正則表達(dá)式...2006-12-12
javascript的正則表達(dá)式學(xué)習(xí)資料復(fù)習(xí)
這里主要記錄幾個(gè)自己之前不是很熟悉的知識(shí)點(diǎn),其他的知識(shí)點(diǎn),上面兩個(gè)網(wǎng)站中已有詳細(xì)的參考2011-07-07
python 正則表達(dá)式獲取字符串中所有的日期和時(shí)間
這篇文章主要介紹了python 正則表達(dá)式獲取字符串中所有的日期和時(shí)間,本文給大家介紹的非常詳細(xì),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2019-10-10
asp 正則表達(dá)式檢測(cè)http開(kāi)頭的函數(shù)
asp 正則表達(dá)式檢測(cè)http開(kāi)頭的函數(shù)...2007-08-08
匹配5到10位無(wú)重復(fù)數(shù)字的正則表達(dá)式
對(duì)于有重復(fù)的5到10位數(shù)字可以使用\d{5,10} 這樣的正則,無(wú)重復(fù)的5到10位數(shù)字我考慮了一下還不會(huì),最然只好查網(wǎng)上,找了段代碼,分享給需要的朋友2012-10-10

