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

