<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>我爱正则表达式 &#187; recursive</title>
	<atom:link href="http://iregex.org/blog/tag/recursive/feed" rel="self" type="application/rss+xml" />
	<link>http://iregex.org</link>
	<description>原创、翻译、转载关于正则表达式的文章</description>
	<lastBuildDate>Sun, 27 Jun 2010 04:20:24 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.0</generator>
<atom:link rel="hub" href="http://pubsubhubbub.appspot.com"/><atom:link rel="hub" href="http://superfeedr.com/hubbub"/><atom:link rel="hub" href="http://www.feedsky.com/api/RPC2"/><atom:link rel="hub" href="http://blogsearch.google.com/ping/RPC2"/><atom:link rel="hub" href="http://blog.yodao.com/ping/RPC2"/><atom:link rel="hub" href="http://www.feedsky.com/api/RPC2"/><atom:link rel="hub" href="http://www.xianguo.com/xmlrpc/ping.php"/><atom:link rel="hub" href="http://www.zhuaxia.com/rpc/server.php"/><atom:link rel="hub" href="http://rpc.technorati.com/rpc/ping"/><atom:link rel="hub" href="http://rpc.pingomatic.com/"/>	
<!-- Start Of Script Generated By WP-PostViews Plus -->
<script type='text/javascript' src='http://iregex.org/wp-includes/js/jquery/jquery.js?ver=1.4.2'></script>
<script type="text/javascript">
/* <![CDATA[ */
/* ]]> */
</script>
<!-- End Of Script Generated By WP-PostViews Plus -->
	<item>
		<title>PHP中的递归正则</title>
		<link>http://iregex.org/blog/recursive-regex-in-php.html</link>
		<comments>http://iregex.org/blog/recursive-regex-in-php.html#comments</comments>
		<pubDate>Mon, 10 May 2010 04:57:28 +0000</pubDate>
		<dc:creator>rex</dc:creator>
				<category><![CDATA[教程]]></category>
		<category><![CDATA[翻译]]></category>
		<category><![CDATA[php]]></category>
		<category><![CDATA[recursive]]></category>

		<guid isPermaLink="false">http://iregex.org/?p=84</guid>
		<description><![CDATA[之前一篇文章翻译了Perl语言中的递归正则表达式. 其实不少语言中的正则都是支持递归的, 例如本文要介绍的PHP正则递归. 虽然, 工作中最常用的正则表达式都很&#8221;正则&#8221;, 只用最基本的语... ]]></description>
			<content:encoded><![CDATA[<p>之前一篇文章翻译了<a title="我爱正则表达式|Perl语言中的递归正则表达式" target="_blank" href="http://iregex.org/blog/recursive-regular-expressions.html">Perl语言中的递归正则表达式</a>. 其实不少语言中的正则都是支持递归的, 例如本文要介绍的PHP正则递归. 虽然, 工作中最常用的正则表达式都很&#8221;正则&#8221;, 只用最基本的语法就能解决85%以上的问题, 而且合理有效地使用普通正则来解决复杂问题也是一门技巧与学问; 但是高级一点的语法的确有它存的价值, 有时不用它还真办不了事儿; 况且学习正则的乐趣也在于<font color="#ff008c">尝试各种各样的可能性, 满足自己无穷无尽的好奇心</font>.</p>
<p><a href="http://iregex.org/blog/recursive-regex-in-php.html" title="我爱正则表达式 | PHP中的递归正则">本文</a>内容, 整理自网文<A HREF="http://www.skdevelopment.com/php-regular-expressions.php">Finer points of PHP regular expressions</A>. 其分析过程剥茧抽丝, 丝丝入扣, 值得一读. 该文系统地列出了PHP中正则表达式常见特性, 我只摘取其中递归部分翻译整理出来. </p>
<p></a><span id="more-84"></span></p>
<h2 style="background-color: rgb(153, 204, 0); line-height: 35px; border: 1px solid rgb(102, 102, 102); color: rgb(0, 0, 0); text-indent: 6px; font-size: 21px;">正文</h2>
<blockquote style="border-left:2px solid #DDDDDD; margin:15px 30px 0 10px; padding-left:20px;">
<h3 style="color: #127ADB; font-size:14px; padding-bottom:3px; padding-top:3px; margin:1.5em 0 1em;">例子</h3>
<blockquote style="border-left:2px solid #DDDDDD; margin:15px 30px 0 10px; padding-left:20px;">
<p>什么时候会用到递归正则表达式呢? 当然是待匹配的字串中递归地出现某种模式时(貌似废话). 最经典的例子, 就是递归正则处理嵌套括号的问题了. 例子如下.  </p>
<p>假设你的文本中包含了正确配对的嵌套括号. 括号的深度可以是无限层. 你想捕获这样的括号组.</p>
<p>恕我剧透, 标准答案是这样的:</p>
<div class="codecolorer-container php mac-classic" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br />6<br />7<br /></div></td><td><div class="php codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #000000; font-weight: bold;">&lt;?php</span><br />
<span style="color: #000088;">$string</span> <span style="color: #339933;">=</span> <span style="color: #0000ff;">&quot;some text (a(b(c)d)e) more text&quot;</span><span style="color: #339933;">;</span><br />
<span style="color: #b1b100;">if</span><span style="color: #009900;">&#40;</span><span style="color: #990000;">preg_match</span><span style="color: #009900;">&#40;</span><span style="color: #0000ff;">&quot;/\(([^()]+|(?R))*\)/&quot;</span><span style="color: #339933;">,</span><span style="color: #000088;">$string</span><span style="color: #339933;">,</span><span style="color: #000088;">$matches</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span><br />
<span style="color: #009900;">&#123;</span><br />
&nbsp; &nbsp; <span style="color: #b1b100;">echo</span> <span style="color: #0000ff;">&quot;&lt;pre&gt;&quot;</span><span style="color: #339933;">;</span> <span style="color: #990000;">print_r</span><span style="color: #009900;">&#40;</span><span style="color: #000088;">$matches</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span> <span style="color: #b1b100;">echo</span> <span style="color: #0000ff;">&quot;&lt;/pre&gt;&quot;</span><span style="color: #339933;">;</span><br />
<span style="color: #009900;">&#125;</span><br />
<span style="color: #000000; font-weight: bold;">?&gt;</span></div></td></tr></tbody></table></div>
<p>其输出结果是:</p>
<div class="codecolorer-container php mac-classic" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br /></div></td><td><div class="php codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #990000;">Array</span><br />
<span style="color: #009900;">&#40;</span><br />
&nbsp; &nbsp; <span style="color: #009900;">&#91;</span><span style="color: #cc66cc;">0</span><span style="color: #009900;">&#93;</span> <span style="color: #339933;">=&gt;</span> <span style="color: #009900;">&#40;</span>a<span style="color: #009900;">&#40;</span>b<span style="color: #009900;">&#40;</span>c<span style="color: #009900;">&#41;</span>d<span style="color: #009900;">&#41;</span>e<span style="color: #009900;">&#41;</span><br />
&nbsp; &nbsp; <span style="color: #009900;">&#91;</span><span style="color: #cc66cc;">1</span><span style="color: #009900;">&#93;</span> <span style="color: #339933;">=&gt;</span> e &nbsp; &nbsp;<br />
<span style="color: #009900;">&#41;</span></div></td></tr></tbody></table></div>
<p>可见, 我们所需要的文本, 已经捕获到<code class="codecolorer php default"><span class="php"><span style="color: #000088;">$matches</span><span style="color: #009900;">&#91;</span><span style="color: #cc66cc;">0</span><span style="color: #009900;">&#93;</span></span></code>中了.</p>
</blockquote>
<h3 style="color: #127ADB; font-size:14px; padding-bottom:3px; padding-top:3px; margin:1.5em 0 1em;">原理</h3>
<blockquote style="border-left:2px solid #DDDDDD; margin:15px 30px 0 10px; padding-left:20px;">
<p>现在思考原理.</p>
<p>上面的正则表达式中的关键点是<code class="codecolorer php default"><span class="php"><span style="color: #009900;">&#40;</span>?R<span style="color: #009900;">&#41;</span></span></code>. <code class="codecolorer php default"><span class="php"><span style="color: #009900;">&#40;</span>?R<span style="color: #009900;">&#41;</span></span></code>的作用就是递归地替换它所在的整条正则表达式. 在每次迭代时, PHP 语法分析器都会将<code class="codecolorer php default"><span class="php"><span style="color: #009900;">&#40;</span>?R<span style="color: #009900;">&#41;</span></span></code>替换为&#8221;<code class="codecolorer php default"><span class="php">\<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#91;</span>^<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#93;</span><span style="color: #339933;">+|</span><span style="color: #009900;">&#40;</span>?R<span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">*</span>\<span style="color: #009900;">&#41;</span></span></code>&#8220;.</p>
<p>因此, 具体到上述的例子, 其正则表达式等价于:</p>
<p>            <code class="codecolorer php default"><span class="php"><span style="color: #0000ff;">&quot;/\(([^()]+|\(([^()]+|\(([^()]+)*\))*\))*\)/&quot;</span></span></code></p>
<p>但是上面的代码只适合深度为3层的括号. 对于未知深度的括号嵌套, 就只好使用这种正则了:</p>
<p>            <code class="codecolorer php default"><span class="php"><span style="color: #0000ff;">&quot;/\(([^()]+|(?R))*\)/&quot;</span></span></code> </p>
<p>它不但能够匹配无限深度, 还简化了正则表达式的语法. 功能强大, 语法简洁.</p>
<p>现在来细看一下<code class="codecolorer php default"><span class="php"><span style="color: #0000ff;">&quot;/\(([^()]+|(?R))*\)/&quot;</span></span></code>是怎样匹配<code class="codecolorer php default"><span class="php"><span style="color: #0000ff;">&quot;(a(b(c)d)e)&quot;</span></span></code>的:</p>
<blockquote style="border-left:2px solid #DDDDDD; margin:15px 30px 0 10px; padding-left:20px;">
<ol>
<li><code class="codecolorer php default"><span class="php"><span style="color: #0000ff;">&quot;(c)&quot;</span></span></code>这部分被正则式 <code class="codecolorer php default"><span class="php"><span style="color: #0000ff;">&quot;\(([^()]+)*\)&quot;</span></span></code> 匹配. 请注意, <code class="codecolorer php default"><span class="php"><span style="color: #009900;">&#40;</span>c<span style="color: #009900;">&#41;</span></span></code> 其实就相当于整个递归的一个缩影, 麻雀虽小五脏俱全, 因此它用到了整个正则表达式.<br />换言之, 下一步中的<code class="codecolorer php default"><span class="php"><span style="color: #009900;">&#40;</span>c<span style="color: #009900;">&#41;</span></span></code>, 可以使用<code class="codecolorer php default"><span class="php"><span style="color: #009900;">&#40;</span>?R<span style="color: #009900;">&#41;</span></span></code> 来匹配.</li>
<li><code class="codecolorer php default"><span class="php"><span style="color: #009900;">&#40;</span>b<span style="color: #009900;">&#40;</span>c<span style="color: #009900;">&#41;</span>d<span style="color: #009900;">&#41;</span></span></code>的匹配过程为:
<ol>
<li><code class="codecolorer php default"><span class="php"><span style="color: #0000ff;">&quot;\(&quot;</span></span></code>匹配<code class="codecolorer php default"><span class="php"><span style="color: #0000ff;">&quot;(&quot;</span></span></code>;</li>
<li><code class="codecolorer php default"><span class="php"><span style="color: #0000ff;">&quot;[^()]+&quot;</span></span></code>匹配<code class="codecolorer php default"><span class="php"><span style="color: #0000ff;">&quot;b&quot;</span></span></code>;</li>
<li><code class="codecolorer php default"><span class="php">&nbsp;<span style="color: #009900;">&#40;</span>?R<span style="color: #009900;">&#41;</span></span></code>匹配<code class="codecolorer php default"><span class="php"><span style="color: #0000ff;">&quot;(c)&quot;</span></span></code>;</li>
<li><code class="codecolorer php default"><span class="php"><span style="color: #0000ff;">&quot;[^()]+&quot;</span></span></code>匹配<code class="codecolorer php default"><span class="php"><span style="color: #0000ff;">&quot;d&quot;</span></span></code>;</li>
<li><code class="codecolorer php default"><span class="php"><span style="color: #0000ff;">&quot;\)&quot;</span></span></code>匹配<code class="codecolorer php default"><span class="php"><span style="color: #0000ff;">&quot;)&quot;</span></span></code>.</li>
</ol>
</li>
</ol>
<p>根据上面的匹配原理, 不难理解为什么数组的第2个元素<code class="codecolorer php default"><span class="php"><span style="color: #000088;">$matches</span><span style="color: #009900;">&#91;</span><span style="color: #cc66cc;">1</span><span style="color: #009900;">&#93;</span></span></code>与<code class="codecolorer php default"><span class="php"><span style="color: #0000ff;">'e'</span></span></code>等价. 子串<code class="codecolorer php default"><span class="php"><span style="color: #0000ff;">'e'</span></span></code>是在最后一次匹配迭代中被捕获. 匹配过程中, <font color="#ff008c">只有最后一次的捕获结果才会保存到数组中</font>.</p>
<blockquote  style="border-left:2px solid #DDDDDD; margin:15px 30px 0 10px; padding-left:20px;"><p>rex注: 关于这个特性, 可以自行尝试一下, 看看使用正则式<code class="codecolorer php default"><span class="php"><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#91;</span>a<span style="color: #339933;">-</span>z<span style="color: #009900;">&#93;</span><span style="color: #339933;">+</span><span style="color: #009900;">&#91;</span><span style="color: #cc66cc;">0</span><span style="color: #339933;">-</span><span style="color: #cc66cc;">9</span><span style="color: #009900;">&#93;</span><span style="color: #339933;">+</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">+</span></span></code>来匹配字串<code class="codecolorer php default"><span class="php">abc123xyz890</span></code>, 其捕获结果<code class="codecolorer php default"><span class="php">$<span style="color: #cc66cc;">1</span></span></code>是什么. 注意, 其结果与 Left Longest 原理并不冲突.</p></blockquote>
</blockquote>
<p>         如果我们只需要捕获 <code class="codecolorer php default"><span class="php"><span style="color: #000088;">$matches</span><span style="color: #009900;">&#91;</span><span style="color: #cc66cc;">0</span><span style="color: #009900;">&#93;</span></span></code>, 可以这样做:</p>
<div class="codecolorer-container php mac-classic" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br />6<br />7<br /></div></td><td><div class="php codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #000000; font-weight: bold;">&lt;?php</span><br />
&nbsp; &nbsp; <span style="color: #000088;">$string</span> <span style="color: #339933;">=</span> <span style="color: #0000ff;">&quot;some text (a(b(c)d)e) more text&quot;</span><span style="color: #339933;">;</span><br />
&nbsp; &nbsp; <span style="color: #b1b100;">if</span><span style="color: #009900;">&#40;</span><span style="color: #990000;">preg_match</span><span style="color: #009900;">&#40;</span><span style="color: #0000ff;">&quot;/\((?:[^()]+|(?R))*\)/&quot;</span><span style="color: #339933;">,</span><span style="color: #000088;">$string</span><span style="color: #339933;">,</span><span style="color: #000088;">$matches</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span><br />
&nbsp; &nbsp; <span style="color: #009900;">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #b1b100;">echo</span> <span style="color: #0000ff;">&quot;&lt;pre&gt;&quot;</span><span style="color: #339933;">;</span> <span style="color: #990000;">print_r</span><span style="color: #009900;">&#40;</span><span style="color: #000088;">$matches</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span> <span style="color: #b1b100;">echo</span> <span style="color: #0000ff;">&quot;&lt;/pre&gt;&quot;</span><span style="color: #339933;">;</span><br />
&nbsp; &nbsp; <span style="color: #009900;">&#125;</span><br />
<span style="color: #000000; font-weight: bold;">?&gt;</span></div></td></tr></tbody></table></div>
<p>产生的结果相同:</p>
<div class="codecolorer-container php mac-classic" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br /></div></td><td><div class="php codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #990000;">Array</span><br />
&nbsp; &nbsp; <span style="color: #009900;">&#40;</span><br />
&nbsp; &nbsp; &nbsp;<span style="color: #009900;">&#91;</span><span style="color: #cc66cc;">0</span><span style="color: #009900;">&#93;</span> <span style="color: #339933;">=&gt;</span> <span style="color: #009900;">&#40;</span>a<span style="color: #009900;">&#40;</span>b<span style="color: #009900;">&#40;</span>c<span style="color: #009900;">&#41;</span>d<span style="color: #009900;">&#41;</span>e<span style="color: #009900;">&#41;</span><br />
&nbsp; &nbsp; <span style="color: #009900;">&#41;</span></div></td></tr></tbody></table></div>
<p>所做的改动是捕获括号<code class="codecolorer php default"><span class="php"><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span></span></code>改为非捕获捕获括号<code class="codecolorer php default"><span class="php"><span style="color: #009900;">&#40;</span>?<span style="color: #339933;">:</span><span style="color: #009900;">&#41;</span></span></code>了.</p>
<p>还可以进一步完善为:</p>
<div class="codecolorer-container php mac-classic" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br />6<br />7<br /></div></td><td><div class="php codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #000000; font-weight: bold;">&lt;?php</span><br />
&nbsp; &nbsp; <span style="color: #000088;">$string</span> <span style="color: #339933;">=</span> <span style="color: #0000ff;">&quot;some text (a(b(c)d)e) more text&quot;</span><span style="color: #339933;">;</span><br />
&nbsp; &nbsp; <span style="color: #b1b100;">if</span><span style="color: #009900;">&#40;</span><span style="color: #990000;">preg_match</span><span style="color: #009900;">&#40;</span><span style="color: #0000ff;">&quot;/\((?&gt;[^()]+|(?R))*\)/&quot;</span><span style="color: #339933;">,</span><span style="color: #000088;">$string</span><span style="color: #339933;">,</span><span style="color: #000088;">$matches</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span><br />
&nbsp; &nbsp; <span style="color: #009900;">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #b1b100;">echo</span> <span style="color: #0000ff;">&quot;&lt;pre&gt;&quot;</span><span style="color: #339933;">;</span> <span style="color: #990000;">print_r</span><span style="color: #009900;">&#40;</span><span style="color: #000088;">$matches</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span> <span style="color: #b1b100;">echo</span> <span style="color: #0000ff;">&quot;&lt;/pre&gt;&quot;</span><span style="color: #339933;">;</span><br />
&nbsp; &nbsp; <span style="color: #009900;">&#125;</span><br />
<span style="color: #000000; font-weight: bold;">?&gt;</span></div></td></tr></tbody></table></div>
<p>这里我们用到了所谓的一次性模式(rex注: 余晟先生译的《精通正则表达式v3.0》中, 谓之&#8221;固化分组&#8221;. 可参考该书.) PHP手册也推荐只要条件允许, 就尽可能使用这种模式, 以便提升正则表达式的速度.</p>
<p>一次性模式很简单, 这里不再详述. 如果感兴趣, 可以参考PHP 官方手册. 如果您想深入学习PERL兼容式正则表达式, 请参考文末链接.<br />
</blockquote>
</blockquote>
<h2  style="background-color: rgb(153, 204, 0); line-height: 35px; border: 1px solid rgb(102, 102, 102); color: rgb(0, 0, 0); text-indent: 6px; font-size: 21px;">提到的链接</h2>
<blockquote style="border-left:2px solid #DDDDDD; margin:15px 30px 0 10px; padding-left:20px;">
<ul>
<li>原文: <a href="http://www.skdevelopment.com/php-regular-expressions.php">Finer points of PHP regular expressions</a></li>
<li>Perl兼容正则表达式 <a href="http://www.pcre.org">官网</a> <a href="http://pcre.org/pcre.txt">文档</a></li>
<li><a href="http://us3.php.net/manual/en/reference.pcre.pattern.syntax.php">PHP官网的PCRE正则文档</a></li>
</ul>
</blockquote>
]]></content:encoded>
			<wfw:commentRss>http://iregex.org/blog/recursive-regex-in-php.html/feed</wfw:commentRss>
		<slash:comments>5</slash:comments>
		</item>
		<item>
		<title>关于从普通文本提取正则表达式的再思考</title>
		<link>http://iregex.org/blog/text-2-regular-expressions-again.html</link>
		<comments>http://iregex.org/blog/text-2-regular-expressions-again.html#comments</comments>
		<pubDate>Mon, 08 Mar 2010 18:32:19 +0000</pubDate>
		<dc:creator>rex</dc:creator>
				<category><![CDATA[应用]]></category>
		<category><![CDATA[array]]></category>
		<category><![CDATA[engine]]></category>
		<category><![CDATA[hash]]></category>
		<category><![CDATA[perl]]></category>
		<category><![CDATA[recursive]]></category>

		<guid isPermaLink="false">http://iregex.org/?p=79</guid>
		<description><![CDATA[rex按： 写完上一篇文章之后，一直在考虑如何真正实现从普通文本中归纳正则表达式的实现。走了许多弯路，也学了不少知识。例如，perl黑豹书上复杂的数据结构、匿名散列和数组、refenrence... ]]></description>
			<content:encoded><![CDATA[<blockquote style="border-left:2px solid #DDDDDD; margin:15px 30px 0 10px; padding-left:20px;"><h3 style="color: #127ADB; font-size:14px; padding-bottom:3px; padding-top:3px; margin:1.5em 0 1em;">rex按：</h3>
<p>写完<a id="vt:b" title="个人应用之明文字串到正则" href="http://iregex.org/blog/literal-text-to-regex.html"><span class="Apple-style-span" style="color: #474747;"><span class="Apple-style-span" style="font-style: normal;">上一篇</span></span></a><span class="Apple-style-span" style="font-style: normal;">文章之后，一直在考虑如何真正实现从普通文本中归纳正则表达式的实现。走了许多弯路，也学了不少知识。例如，perl黑豹书上复杂的数据结构、匿名散列和数组、refenrence；紫龙书上的状态机的构造，数据结构上图论的知识，都是很有用的。另外还新学了</span><a id="mk58" title="graphviz" href="http://www.graphviz.org/"><span class="Apple-style-span" style="font-style: normal;">graphviz</span></a><span class="Apple-style-span" style="font-style: normal;">的用法。以前觉得很神秘，不过一用才发现很直观。本文的插图是使用</span><a id="a1la" title="online版本的graphviz" href="http://graph.gafol.net/create"><span class="Apple-style-span" style="color: #474747;"><span class="Apple-style-span" style="font-style: normal;">online版本的graphviz</span></span></a><span class="Apple-style-span" style="font-style: normal;">画的。</span></p>
<p>除了本文的这种实现方法（基于图），我还使用另一种方式实现了，很简单：基于关键词。具体作法是，逐一读取每一行文本，使用\s+等将其split开，形成array；然后再对所有的array进行求交集的操作（使用hash），得到每一行都有的关键词；然后按从左到右的顺序建立这覆的正则式^(.*?)keyword1(.*?)keyword2&#8230;.keywordN(.*?)$，再分别匹配每一行文本，得到hash的hash表，或者array的array，转置，并列输出，得到^(option1|option2&#8230;)keyword1(option..)&#8230;$这样的正则式。最后作为验证，再将所最终生成的正则与每一行匹配测试一下。</p>
<p>这样以词为单位做完之后，再逐个字母地分隔开来，递归地处理<span class="Apple-style-span" style="color: #474747;"><span class="Apple-style-span" style="font-style: normal;">(option1|option2&#8230;)的部分。先是单词级，再是字母级，有利于先在最大程度上找出重复的内容；而且粗化和细化的处理过程，思路是一致的，粒度不同罢了。</span></span></p>
<p><span class="Apple-style-span" style="color: #474747;"> 新手请自重，高手请赐教，我的思路未必是正确或最优的。</span></p></blockquote>
<p><span id="more-79"></span></p>
<h2 style="background-color:#99CC00; font-size:14px; padding-bottom:3px; padding-left:10px; padding-top:3px;  line-height:1.5em; margin:1.5em 0 1em;">问题</h2>
<blockquote style="border-left:2px solid #DDDDDD; margin:15px 30px 0 10px; padding-left:20px;"><div>有文本文件text.txt，内容如下：</div>
<blockquote style="border-left:2px solid #DDDDDD; margin:15px 30px 0 10px; padding-left:20px;">
<div>this is a red fox</div>
<div>this is a blue firefox</div>
<div>this is a pig</div>
<div>a red fox</div>
</blockquote>
<div>请写一则程序，根据文本内容，自动构造（比较合理的）正则表达式，使之能够匹配文件中<strong>每一行</strong>文本。</div>
</blockquote>
<h2 style="background-color:#99CC00; font-size:14px; padding-bottom:3px; padding-left:10px; padding-top:3px;  line-height:1.5em; margin:1.5em 0 1em;">标准正则</h2>
<blockquote style="border-left:2px solid #DDDDDD; margin:15px 30px 0 10px; padding-left:20px;"><p>有两种极端的解法是不可取的：</p>
<ol>
<li><span class="Apple-style-span" style="color: #ff00ff;">^.*$</span></li>
<li><span class="Apple-style-span" style="color: #ff00ff;">^(this is a red fox|this is a blue firefox|this is a pig|a red fox)$</span></li>
</ol>
<p>第一种失之于太宽泛，第二种失之于太狭隘。太宽泛则泥沙俱下，无论什么文本都能匹配；太狭隘则僵化死板，缺乏灵活性。好的正则表达式源于例文本（从例文本中提取规律），又高于例文本（能匹配同规律的其它文本）。匹配什么，排除什么，都有定则，所谓“君子有所为而有所不为”，指的就是这种情况（貌似跑题了:)）。</p>
<div>那么，如何是比较靠谱的正则表达式呢？以上文的例子而言，可以是：</div>
<div><span class="Apple-style-span" style="color: #ff00ff;">^(this is )?a (red fox|blue firefox|pig)$</span></div>
<div>现在我们向着标准答案出发。</div>
</blockquote>
<h2 style="background-color:#99CC00; font-size:14px; padding-bottom:3px; padding-left:10px; padding-top:3px;  line-height:1.5em; margin:1.5em 0 1em;">思路</h2>
<blockquote style="border-left:2px solid #DDDDDD; margin:15px 30px 0 10px; padding-left:20px;"><div>任何复杂的电路图，都可以拆分为三种简单的关系：串联，并联，短路。正则表达式也同理。</div>
<p>既然是一条正则匹配所有的文本，那么这条正则（记为<span class="Apple-style-span" style="color: #ff00ff;">$re</span>）也应该匹配第一行文本。</p>
<p>第一行文本为this is a red fox。那么，从<span class="Apple-style-span" style="color: #ff00ff;">^this is a red fox$</span>应该是<span class="Apple-style-span" style="color: #ff00ff;">$re</span>的一个（真）子集。它的路径为：<span class="Apple-style-span" style="color: #ff00ff;">&#8220;^&#8221;-&gt;this-&gt;is-&gt;a-&gt;red-&gt;fox-&gt;&#8221;$&#8221;</span>。全部节点之间，是串联关系，从左到右依次排列即可。</p>
<p>示意图如下(可以点击看全尺寸图，下同)：</p>
<p><a style="color: #0071bb; margin-left: 0px; margin-right: 0px;" href="http://i293.photobucket.com/albums/mm60/zhasm/iregex/20100309001329.png" target="_blank"><img style="margin-left: 0px; margin-right: 0px;" src="http://i293.photobucket.com/albums/mm60/zhasm/iregex/20100309001329.png" border="0" alt="Photobucket" /></a></p>
<p>同理，第二行文本也应该是<span class="Apple-style-span" style="color: #ff00ff;">$re</span>的子集。不过，由于已经存在了由<span class="Apple-style-span" style="color: #ff00ff;">^-&gt;this-&gt;is-&gt;a</span>的路径，到a时出现支路，<span class="Apple-style-span" style="color: #ff00ff;">a-&gt;blue-&gt;firefox-&gt;$</span>；</p>
<p>将此路径添加到示意图上，得到：</p>
<p><a style="color: #0071bb; margin-left: 0px; margin-right: 0px;" href="http://i293.photobucket.com/albums/mm60/zhasm/iregex/20100309001747.png" target="_blank"><img style="border-color: initial; border-style: initial; margin-left: 0px; margin-right: 0px;" src="http://i293.photobucket.com/albums/mm60/zhasm/iregex/20100309001747.png" border="0" alt="Photobucket" /></a></p>
<p>显而易见，这两条并列的支路，始于a，终于$，可以使用|来并列之。</p>
<p>好了，我们总结一下规律：</p>
<div><span class="Apple-style-span" style="font-family: arial,sans-serif;"><span class="Apple-style-span" style="color: #000000;"><strong><span class="Apple-style-span"><span class="Apple-style-span" style="background-color: #6fa8dc;">并列</span></span></strong>：如果存在A-&gt;B-&gt;C，且同时存在A-&gt;D-&gt;C，则B与D之间是并联关系。即出发点相同，结束点相同，且出发点与结束点之间各有一个以上的节点。并列使用括号来表示，之间以|分隔。例如，对于<span class="Apple-style-span" style="font-family: arial,sans-serif;"><span class="Apple-style-span" style="color: #000000;">A-&gt;B-&gt;C，A-&gt;D-&gt;C，则可以使用A(B|D)C来表示其正则关系。</span></span></span></span></div>
<div><span class="Apple-style-span" style="font-family: arial,sans-serif;"><span class="Apple-style-span" style="color: #000000;">为什么要强调是一个以上节点呢？这里先卖个关子。请继续阅读。</span></span></div>
<p>再往下，this is a pig，同理，只需要在原图基础上添加<span class="Apple-style-span" style="color: #ff00ff;">a-&gt;pig-&gt;$</span>的支路即可。此时图示如下：</p>
<p><a style="color: #0071bb; margin-left: 0px; margin-right: 0px;" href="http://i293.photobucket.com/albums/mm60/zhasm/iregex/20100309002851.png" target="_blank"><img style="border-color: initial; border-style: initial; margin-left: 0px; margin-right: 0px;" src="http://i293.photobucket.com/albums/mm60/zhasm/iregex/20100309002851.png" border="0" alt="Photobucket" /></a></p>
<div>
最后一条，a red fox。这条貌似复杂，但是只需在<span class="Apple-style-span" style="color: #ff00ff;">^-&gt;a</span>之间新添加了一条路径而已；<span class="Apple-style-span" style="color: #ff00ff;">a-&gt;red-&gt;fox-&gt;$</span>之间原有路径，可以继续使用。此时，得到完整的示意图如下：</div>
<p><a style="color: #ed1e24; margin-left: 0px; margin-right: 0px; text-decoration: none;" href="http://i293.photobucket.com/albums/mm60/zhasm/iregex/20100309003225.png" target="_blank"><img style="border-color: initial; border-style: initial; margin-left: 0px; margin-right: 0px;" src="http://i293.photobucket.com/albums/mm60/zhasm/iregex/20100309003225.png" border="0" alt="Photobucket" /></a></p>
<p>此时，观察可知，一种新的情况出现了。同时存在<span class="Apple-style-span" style="color: #ff00ff;">^-&gt;a</span>，和<span class="Apple-style-span" style="color: #ff00ff;">&#8220;^&#8221;-&gt;this-&gt;is-&gt;a</span>两条路径。想一下初中物理电路图，我们可以将这种情况称为“短路”，即，<span class="Apple-style-span" style="color: #ff00ff;">&#8220;^&#8221;-&gt;this-&gt;is-&gt;a</span>这个线路的^、a两个节点之间，添加了一条无障碍通道，它能无视this、is的存在，因此，让<span class="Apple-style-span" style="color: #ff00ff;">this-&gt;is</span>这条路径成为<strong>可选项</strong>。再总结一下规律：</p>
<p>如果有A-&gt;B-&gt;&#8230;C-&gt;D的路径，且有A-&gt;D的路径，则称A-&gt;D之间存在短路，此时,B-&gt;&#8230;-&gt;C可以用(B-&gt;&#8230;-&gt;C)?来表示(就是用括号来表示被短路的部分，问号表示短路之)。</p>
<div>顶点A,D之间，最多存在一个短路关系。但是可以有1或更多条并列的关系存在。</div>
<div>好了，分析结束，得到这样的正则式：</div>
<div><span class="Apple-style-span" style="color: #ff00ff;">^(this is )?a (red fox|blue firefox|pig)$</span></div>
<p>这也就是为什么上文要强调是一个节点的缘故。</p>
<div>
<p>如果我们再精益求精的话，可以对<span class="Apple-style-span" style="color: #ff00ff;">red fox|blue firefox|pig</span>这部分<strong><span class="Apple-style-span" style="color: #ff00ff;">递归地</span></strong>进行上述分析过程，进而得到<span class="Apple-style-span" style="color: #ff00ff;"> (red |blue fire)fox|pig</span>这样的结果。</p></blockquote>
<h2 style="background-color:#99CC00; font-size:14px; padding-bottom:3px; padding-left:10px; padding-top:3px;  line-height:1.5em; margin:1.5em 0 1em;">实现</h2>
<blockquote style="border-left:2px solid #DDDDDD; margin:15px 30px 0 10px; padding-left:20px;"><div>思路有了，编程就简单了。perl中，固然可以使用比较简洁的hash表来表示链表之间的关系：</div>
<div>例如：</div>
<div>my $hash;</div>
<div style="margin-left: 0px; margin-right: 0px;">$$hash{&#8220;^&#8221;}{&#8220;this&#8221;}{&#8220;is&#8221;}{&#8220;a&#8221;}{&#8220;red&#8221;}{&#8220;fox&#8221;}{&#8220;\$&#8221;}=&#8221;";</div>
<div>$$hash{&#8220;^&#8221;}{&#8220;this&#8221;}{&#8220;is&#8221;}{&#8220;a&#8221;}{&#8220;blue&#8221;}{&#8220;firefox&#8221;}{&#8220;\$&#8221;}=&#8221;";</div>
<p>&#8230;</p>
<div>但是，节点的增删修改都是麻烦事。（我在hash迷宫中lost了很久才爬出来）</div>
<div>抽空补了一下<strong>有向图</strong>的知识，觉得可以简化问题如下。</div>
<p><a style="color: #ed1e24; margin-left: 0px; margin-right: 0px; text-decoration: none;" href="http://i293.photobucket.com/albums/mm60/zhasm/iregex/20100309003225.png" target="_blank"><img style="margin-left: 0px; margin-right: 0px;" src="http://i293.photobucket.com/albums/mm60/zhasm/iregex/20100309003225.png" border="0" alt="Photobucket" /></a></p>
<p>上图其实是一个有向图，只需记录所有的顶点集合，路径集合，再来求各路径之间的关系；最后打印输出，即是所求。</p>
<div>顶点集合为：</div>
<div><span class="Apple-style-span" style="color: #ff00ff;">(^, this, is, a, red, fox, blue, firefox, pig, $);</span></div>
<div>通路关系集合为：</div>
<div><span class="Apple-style-span" style="color: #ff00ff;">(^-&gt;this, this-&gt;is,&#8230;)</span></div>
<p>这两个集合在读取文本文件行的时候可以一次性建立。不复杂。关键是关系的确立。</p>
<div>再次总结，如下：</div>
<ul>
<li>从一个顶点A出发的N条支路必定汇合（只是有时是同一个点，有时不在同一点而已。本文给出的例子是最简单的情况，这里可以假设为汇合到同一点）于M点。</li>
<li>这N条路中，每一条路径的长短以经过的节点个数来计算。例如上图中，^到a有一条路，上面的路径为2，下面的路径为0。</li>
<li>短的支路决定了这N条支路的关系。</li>
<li>长度为任意两点之间，最多只可能有一条长度为0的边。</li>
<li>如果存在长度为0的边，则其余的同级的支路被短路。</li>
<li>长度不为0的N-1条支路之间是并列关系。</li>
<li>整个图始于^，终于$。</li>
</ul>
<div>这些条件、判断，均可以细化为函数。具体的程序从略。</div>
</blockquote>
]]></content:encoded>
			<wfw:commentRss>http://iregex.org/blog/text-2-regular-expressions-again.html/feed</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>[译]递归正则表达式</title>
		<link>http://iregex.org/blog/recursive-regular-expressions.html</link>
		<comments>http://iregex.org/blog/recursive-regular-expressions.html#comments</comments>
		<pubDate>Wed, 16 Dec 2009 15:06:47 +0000</pubDate>
		<dc:creator>rex</dc:creator>
				<category><![CDATA[翻译]]></category>
		<category><![CDATA[perl]]></category>
		<category><![CDATA[recursive]]></category>

		<guid isPermaLink="false">http://iregex.org/?p=73</guid>
		<description><![CDATA[原文在此。rex译于2009年12月15～17日，翻译过程中使用的是google docs@prism@firefox@ubuntu 9.10，很爽的体验。感谢余晟老师在正则和翻译方面的悉心指导。 平时我们用到的正则表达式，其实没那么“... ]]></description>
			<content:encoded><![CDATA[<blockquote style="border-left:2px solid #DDDDDD; margin:15px 30px 0 10px; padding-left:20px;"><p>原文<a title="在此" href="http://www.catonmat.net/blog/recursive-regular-expressions/" id="eklo">在此</a>。<a href="http://iregex.org">rex</a>译于2009年12月15～17日，翻译过程中使用的是google docs@prism@firefox@ubuntu 9.10，很爽的体验。感谢<a href="http://www.luanxiang.org/blog/">余晟</a>老师在正则和翻译方面的悉心指导。</p></blockquote>
<p><a href="http://iregex.org/blog/recursive-regular-expressions.html" target="_blank"><img src="http://i293.photobucket.com/albums/mm60/zhasm/yo-dawg-regex.jpg" alt="递归正则表达式" border="0"></a></p>
<p>平时我们用到的正则表达式，其实没那么“规则”。多数编程语言所支持的扩展的正则表达式，其运算能力比起<a title="形式语言理论" href="http://en.wikipedia.org/wiki/Formal_language" id="vcy6">形式语言理论</a>所定义的“<a title="规则" href="http://en.wikipedia.org/wiki/Regular_expression" id="gkvv">规则</a>”正则表达式要强得多。</p>
<p><span id="more-73"></span></p>
<blockquote style="border-left:2px solid #DDDDDD; margin:15px 30px 0 10px; padding-left:20px;"><p>rex注：在这里其实可以看到，如果将<b>正则表达式</b>译为<b>正规表达式</b>，一切就都通顺了：<br />
平时我们用到的正规表达式，其实没那么“正规”。多数编程语言所支持的扩展的正规表达式，其运算能力比起正式语言理论所定义的“正规”正规表达式要强得多。</p>
<p>regular expression的日文是“正规表现”，在鸟哥书中，好像也将其称为正规表达式。<a title="via" href="http://fanfou.alwaysdata.net/status&amp;id=5144919240" id="bg:v">via</a></p>
</blockquote>
<p>例如，经常用到的<a title="捕获缓存" href="http://perldoc.perl.org/perlre.html#Capture-buffers" id="emj9">捕获缓存</a>，就是用来帮助我们临时存储任意正则表达式模式，以便重复使用。 又如，“<a title="环视断言" href="http://perldoc.perl.org/perlre.html#Look-Around-Assertions" id="xsz-">环视断言</a>”能让正则表达式引擎在做决定之前先偷偷看看环视一下。这些扩展让正则表达式非常强大，足以描述一些“<a title="上下文无关语法" href="http://en.wikipedia.org/wiki/Context-free_grammar" id="acrs">上下文无关语法</a>”。</p>
<p>Perl语言的正则表达式引擎特性异常丰富，其特征之一是<strong>懒惰正则子表达式</strong>（Lazy regular subexpressions），格式为(??{code})，其中的“code”可以是任意一段perl程序，该子表达式可能匹配时，这段程序就会执行。</p>
<p>我们可以利用这一特征来编写出非常有趣的东西，即将正则表达式自身嵌在它的“code”部分，由此生成<b>递归的正则表达式</b>(a recursive regular expression)！</p>
<p>一直以来，正则表达式无法匹配0<sup>n</sup>1<sup>n</sup>这种表达式，也就是由若干个0以及同等数量的1所组成的字符串。如果使用懒惰正则子表达式，这一经典问题就迎刃而解。</p>
<p>下面是匹配0<sup>n</sup>1<sup>n</sup>字串的perl正则表达式代码。</p>
<div class="codecolorer-container perl mac-classic" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br /></div></td><td><div class="perl codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #0000ff;">$regex</span> <span style="color: #339933;">=</span> <span style="color: #009966; font-style: italic;">qr/0(??{$regex})?1/</span><span style="color: #339933;">;</span></div></td></tr></tbody></table></div>
<blockquote style="border-left:2px solid #DDDDDD; margin:15px 30px 0 10px; padding-left:20px;"><p>rex注：紫龙书第四章有云：“文法是比正则表达式表达能力更强的表示方法。每个可以使用正则表达式描述的构造，都可以使用文法来描述，但是反之不成立。换句话说，每个正则语言都是一个上下文无关语言，但是反之不成立。”书中交待的正则，也应该是指的常规正则表达式，而非现代语言中的扩展的正则表达式。一般使用正则表达式来构造小部件，而使用文法来组建语言框架。
</p></blockquote>
<p>此正则表达式匹配一个字符0，之后是正则表达式自身0或1次，之后是字符1。如果正则表达式自身部分不能匹配，那么它只能匹配01；如果自身部分可以匹配，则正则表达式匹配的是00($regex)?11，此时若不能匹配自身则结果是0011，若可以匹配就是000($regex)?111，……依次顺延。</p>
<p>下面是匹配0<sup>50000</sup>1<sup>50000</sup>的Perl程序：</p>
<div class="codecolorer-container perl mac-classic" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br />6<br />7<br />8<br />9<br />10<br />11<br /></div></td><td><div class="perl codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #666666; font-style: italic;">#!/usr/bin/perl &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </span><br />
<br />
<span style="color: #0000ff;">$str</span> <span style="color: #339933;">=</span> <span style="color: #ff0000;">&quot;0&quot;</span>x50000 <span style="color: #339933;">.</span> <span style="color: #ff0000;">&quot;1&quot;</span>x50000<span style="color: #339933;">;</span><br />
<span style="color: #0000ff;">$regex</span> <span style="color: #339933;">=</span> <span style="color: #009966; font-style: italic;">qr/0(??{$regex})*1/</span><span style="color: #339933;">;</span><br />
<br />
<span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span><span style="color: #0000ff;">$str</span> <span style="color: #339933;">=~</span> <span style="color: #009966; font-style: italic;">/^$regex$/</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span><br />
&nbsp; <span style="color: #000066;">print</span> <span style="color: #ff0000;">&quot;yes, it matches&quot;</span><span style="color: #339933;">;</span><br />
<span style="color: #009900;">&#125;</span><br />
<span style="color: #b1b100;">else</span> <span style="color: #009900;">&#123;</span><br />
&nbsp; <span style="color: #000066;">print</span> <span style="color: #ff0000;">&quot;no, it doesn't match&quot;</span><span style="color: #339933;">;</span><br />
<span style="color: #009900;">&#125;</span></div></td></tr></tbody></table></div>
<p>现在来看题图所示的Yo Dawg正则表达式。你先猜猜它的作用？正确答案是，它匹配(foo(bar())baz)这样完全嵌套的括号表达式（fully parenthesized expression）或((()()())())这样的平衡括号表达式（balanced parentheses）。</p>
<div class="codecolorer-container perl mac-classic" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br />2<br />3<br />4<br />5<br />6<br />7<br />8<br />9<br /></div></td><td><div class="perl codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #0000ff;">$regex</span> <span style="color: #339933;">=</span> <span style="color: #000066;">qr</span><span style="color: #339933;">/</span><br />
&nbsp; \<span style="color: #009900;">&#40;</span> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #666666; font-style: italic;"># (1) match an open paren ( &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; #(1)，此处匹配开括号</span><br />
&nbsp; &nbsp; <span style="color: #009900;">&#40;</span> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span style="color: #666666; font-style: italic;"># followed by &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;#(2)，紧接着是</span><br />
&nbsp; &nbsp; &nbsp; <span style="color: #009900;">&#91;</span><span style="color: #339933;">^</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#93;</span><span style="color: #339933;">+</span> &nbsp; &nbsp; &nbsp; <span style="color: #666666; font-style: italic;"># &nbsp; (3) one or more non-paren character &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;#(3)，1个或多个非括号字符</span><br />
&nbsp; &nbsp; <span style="color: #339933;">|</span> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #666666; font-style: italic;"># OR &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;#(4)，或</span><br />
&nbsp; &nbsp; &nbsp; <span style="color: #009900;">&#40;</span><span style="color: #339933;">??</span><span style="color: #009900;">&#123;</span><span style="color: #0000ff;">$regex</span><span style="color: #009900;">&#125;</span><span style="color: #009900;">&#41;</span> &nbsp; <span style="color: #666666; font-style: italic;"># &nbsp; (5) the regex itself &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; #(5)，正则式自身</span><br />
&nbsp; &nbsp; <span style="color: #009900;">&#41;</span><span style="color: #339933;">*</span> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span style="color: #666666; font-style: italic;"># (6) repeated zero or more times &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; #(6)，重复0或多次</span><br />
&nbsp; \<span style="color: #009900;">&#41;</span> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #666666; font-style: italic;"># (7) followed by a close paren ) &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; #(7)，紧接着是闭括号</span><br />
<span style="color: #339933;">/</span>x<span style="color: #339933;">;</span></div></td></tr></tbody></table></div>
<blockquote style="border-left:2px solid #DDDDDD; margin:15px 30px 0 10px; padding-left:20px;"><p>rex注：关于Yo Dawg图片的含义，可以参考<a title="这里" href="http://yoyodawgdawg.com/about" id="az.x">这里</a>。基本上是全是“Yo dawg, I herd you like X, so we put a Y in your Y so you can Z while you Z”的结构的配图文字。</p></blockquote>
<p>构造此正则表达式的思路是这样的。对于完全嵌套的括号表达式，它的开始字符是一个开括号。这是最简单的一步，我们直接写出（上面程序中的(1)）。同理，它的结束字符是闭括号，于是得到(7)。现在该动脑筋了，括号中间是什么呢？对，它可以是既不是开括号又不是闭括号的任意字符（第(3)点），<b>也可以是</b>另一个完全嵌套的表达式（即第(5)点）。所有的这些，既可以只匹配0次（第(3)点），以便构造最小的完全嵌套括号表达式()，也可以匹配多次来匹配较复杂的表达式。</p>
<p>去掉 /x 选项（即，不再使用多行风格的注释模式），可以简记为：</p>
<div class="codecolorer-container perl mac-classic" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br /></div></td><td><div class="perl codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #0000ff;">$regex</span> <span style="color: #339933;">=</span> <span style="color: #009966; font-style: italic;">qr/\(([^()]+|(??{$regex}))*\)/</span><span style="color: #339933;">;</span></div></td></tr></tbody></table></div>
<p>但是切勿在正式产品中使用这一特性，它太诡异，不好把握。建议使用较稳定成熟的<a href="http://search.cpan.org/dist/Text-Balanced/">Text::Balanced</a> 或 <a href="http://search.cpan.org/dist/Regexp-Common/">Regexp::Common</a> 模块。</p>
<blockquote style="border-left:2px solid #DDDDDD; margin:15px 30px 0 10px; padding-left:20px;"><p>rex注：对于(??{code})，perl<a href="http://perldoc.perl.org/perlre.html#%28??{-code-}%29">官方的提示</a>是：此正则表达式仅作测试使用，可能有更新而不作提示。代码执行时产生的副作用，因版本而异，运行结果或有不同，取决于正则引擎的后期优化。</p></blockquote>
<p>最后提醒大家，在Perl 5.10中已经可以使用<a title="递归捕获缓存" href="http://perldoc.perl.org/perlre.html#%28?PARNO%29-%28?-PARNO%29-%28?+PARNO%29-%28?R%29-%28?0%29" id="vchb">递归捕获缓存</a>来替代懒惰代码子表达式了，运行结果相同。</p>
<p>下面是匹配0<sup>n</sup>1<sup>n</sup>的递归捕获缓存语法(?N)的实现：</p>
<div class="codecolorer-container perl mac-classic" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;"><table cellspacing="0" cellpadding="0"><tbody><tr><td style="padding:5px;text-align:center;color:#888888;background-color:#EEEEEE;border-right: 1px solid #9F9F9F;font: normal 12px/1.4em Monaco, Lucida Console, monospace;"><div>1<br /></div></td><td><div class="perl codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"><span style="color: #b1b100;">my</span> <span style="color: #0000ff;">$rx</span> <span style="color: #339933;">=</span> <span style="color: #009966; font-style: italic;">qr/(0(?1)*1)/</span><span style="color: #339933;">;</span></div></td></tr></tbody></table></div>
<p>(?1)*的含义是“匹配第一组0或多次”，这里的第一组是指整个正则表达式。</p>
<p>请自行动手，重写平衡括号的正则表达式，当作练习。</p>
<p>祝玩得开心！</p>
]]></content:encoded>
			<wfw:commentRss>http://iregex.org/blog/recursive-regular-expressions.html/feed</wfw:commentRss>
		<slash:comments>11</slash:comments>
		</item>
	</channel>
</rss>
