<?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; array</title>
	<atom:link href="http://iregex.org/blog/tag/array/feed" rel="self" type="application/rss+xml" />
	<link>http://iregex.org</link>
	<description>原创、翻译、转载关于正则表达式的文章</description>
	<lastBuildDate>Tue, 29 Mar 2011 05:04:10 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.3</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/"/>		<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>4</slash:comments>
		</item>
	</channel>
</rss>

