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