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)題也是一門技巧與學(xué)問(wèn); 但是高級(jí)一點(diǎn)的語(yǔ)法的確有它存的價(jià)值, 有時(shí)不用它還真辦不了事兒; 況且學(xué)習(xí)正則的樂(lè)趣也在于嘗試各種各樣的可能性, 滿足自己無(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ú)限分類,不使用數(shù)據(jù)庫(kù),不使用遞歸.
- php遞歸實(shí)現(xiàn)無(wú)限分類生成下拉列表的函數(shù)
- php實(shí)現(xiàn)無(wú)限級(jí)分類實(shí)現(xiàn)代碼(遞歸方法)
- PHP 無(wú)限分類三種方式 非函數(shù)的遞歸調(diào)用!
- 淺析PHP遞歸函數(shù)返回值使用方法
- PHP遞歸算法的詳細(xì)示例分析
- php遞歸使用示例(php遞歸函數(shù))
- php function用法如何遞歸及return和echo區(qū)別
- php+mysql不用遞歸實(shí)現(xiàn)的無(wú)限級(jí)分類實(shí)例(非遞歸)
- php無(wú)限極分類遞歸排序?qū)崿F(xiàn)方法
- php使用遞歸計(jì)算文件夾大小
相關(guān)文章
python實(shí)現(xiàn)統(tǒng)計(jì)漢字/英文單詞數(shù)的正則表達(dá)式
一個(gè)簡(jiǎn)單的程序,統(tǒng)計(jì)文本文檔中的單詞和漢字?jǐn)?shù),逆序排列(出現(xiàn)頻率高的排在最前面)python實(shí)現(xiàn)2012-09-09ASP 正則函數(shù)替換分頁(yè)后的參數(shù)
在分頁(yè)系統(tǒng)里面用到的把page后面得東西都給丟掉的正則代碼。需要的朋友可以參考下。2009-10-10