<?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>简单生活 -- Kevin Yang的博客 &#187; 正则表达式</title>
	<atom:link href="http://www.imkevinyang.com/tags/%e6%ad%a3%e5%88%99%e8%a1%a8%e8%be%be%e5%bc%8f/feed" rel="self" type="application/rss+xml" />
	<link>http://www.imkevinyang.com</link>
	<description>It&#039;s all about sharing</description>
	<lastBuildDate>Sun, 05 Feb 2012 15:37:14 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.0.1</generator>
		<item>
		<title>Java/Js如何使用正则表达式匹配嵌套Html标签</title>
		<link>http://www.imkevinyang.com/2010/07/javajs%e5%a6%82%e4%bd%95%e4%bd%bf%e7%94%a8%e6%ad%a3%e5%88%99%e8%a1%a8%e8%be%be%e5%bc%8f%e5%8c%b9%e9%85%8d%e5%b5%8c%e5%a5%97html%e6%a0%87%e7%ad%be.html</link>
		<comments>http://www.imkevinyang.com/2010/07/javajs%e5%a6%82%e4%bd%95%e4%bd%bf%e7%94%a8%e6%ad%a3%e5%88%99%e8%a1%a8%e8%be%be%e5%bc%8f%e5%8c%b9%e9%85%8d%e5%b5%8c%e5%a5%97html%e6%a0%87%e7%ad%be.html#comments</comments>
		<pubDate>Fri, 30 Jul 2010 08:02:00 +0000</pubDate>
		<dc:creator>Kevin Yang</dc:creator>
				<category><![CDATA[其他随笔]]></category>
		<category><![CDATA[Html嵌套标签匹配]]></category>
		<category><![CDATA[Java]]></category>
		<category><![CDATA[Javascript]]></category>
		<category><![CDATA[正则表达式]]></category>

		<guid isPermaLink="false">http://www.imkevinyang.com/2010/07/javajs%e5%a6%82%e4%bd%95%e4%bd%bf%e7%94%a8%e6%ad%a3%e5%88%99%e8%a1%a8%e8%be%be%e5%bc%8f%e5%8c%b9%e9%85%8d%e5%b5%8c%e5%a5%97html%e6%a0%87%e7%ad%be.html</guid>
		<description><![CDATA[<p>以前写过一篇文章讲解如何使用正则表达式完美解决Html嵌套标签的匹配问题（<a title="使用正则表达式匹配嵌套Html标签" href="http://www.imkevinyang.com/2009/07/%E4%BD%BF%E7%94%A8%E6%AD%A3%E5%88%99%E8%A1%A8%E8%BE%BE%E5%BC%8F%E5%8C%B9%E9%85%8D%E5%B5%8C%E5%A5%97html%E6%A0%87%E7%AD%BE.html" target="_blank">使用正则表达式匹配嵌套Html标签</a>），但是里头用到了平衡组这样的高级特性，貌似只有DotNet还有Perl正则引擎支持，因此通用性不高。有朋友留言说Java直接使用的话会报错。我后来查了一下，发现Java正则引擎支持的特性相对&#8230;</p>]]></description>
			<content:encoded><![CDATA[<p>以前写过一篇文章讲解如何使用正则表达式完美解决Html嵌套标签的匹配问题（<a title="使用正则表达式匹配嵌套Html标签" href="http://www.imkevinyang.com/2009/07/%E4%BD%BF%E7%94%A8%E6%AD%A3%E5%88%99%E8%A1%A8%E8%BE%BE%E5%BC%8F%E5%8C%B9%E9%85%8D%E5%B5%8C%E5%A5%97html%E6%A0%87%E7%AD%BE.html" target="_blank">使用正则表达式匹配嵌套Html标签</a>），但是里头用到了平衡组这样的高级特性，貌似只有DotNet还有Perl正则引擎支持，因此通用性不高。有朋友留言说Java直接使用的话会报错。我后来查了一下，发现Java正则引擎支持的特性相对比较少。在1.6版本中不能使用命名组（貌似1.7的时候开始支持了），否则会报以下错误，更别说平衡组了。因此感觉要实现无限级的嵌套匹配不大现实。</p>
<blockquote><p>java.util.regex.PatternSyntaxException: Look-behind group does not have an obvious maximum length near index<span style="background-color: #ffffff;"> XX</span></p></blockquote>
<p>在网上搜了好久也没找到完美的解决方案。不过，我们可以实现有限级Html嵌套标签匹配。思路相对于无限级来说就简单了好多，不需要那么多高级的特性。</p>
<p>示例：</p>
<pre class="brush: html">&lt;div id='container'&gt;
    &lt;div style='background-color:gray;' id='footer'&gt;
        &lt;a id='gotop' href='#' onclick='MGJS.goTop();return false;'&gt;Top&lt;/a&gt;
        &lt;a id='powered' href='http://wordpress.org/'&gt;WordPress&lt;/a&gt;
        &lt;div id='copyright'&gt;
            Copyright &amp;copy; 2009 简单生活 —— Kevin Yang的博客
         &lt;/div&gt;
        &lt;div id='themeinfo'&gt;
            Theme by &lt;a href='http://www.neoease.com/'&gt;mg12&lt;/a&gt;. Valid &lt;a href='http://validator.w3.org/check?uri=referer'&gt;XHTML 1.1&lt;/a&gt;
            and &lt;a href='http://jigsaw.w3.org/css-validator/'&gt;CSS 3&lt;/a&gt;.
        &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;</pre>
<p>在上面这个示例中，我们打算匹配id为footer的这个嵌套div，而且<strong>假设我们预先知道footer这个div里面最多只会嵌套一级div</strong>。更多级的情况我们一会儿再讲。</p>
<p>footer的开始和结束标签匹配很简单：</p>
<blockquote><p>&lt;div [^&gt;]*id='footer'[^&gt;]*&gt;......(这里的省略号是一会要填写的)&lt;/div&gt;</p></blockquote>
<p>夹在开始和结束标签之间的内容无非有两种情况：</p>
<ul>
<li>内容A: div标签，并且此div内无嵌套div</li>
<li>内容B: 任意其他内容</li>
</ul>
<p>然后就是这两种内容的不断重复而已。正则表示如下：</p>
<blockquote><p>(<span style="color: #ff0000;">&lt;div[^&gt;]*&gt;.*?&lt;/div&gt;</span>|<span style="color: #008000;"><strong>.</strong></span>)*?</p></blockquote>
<p>注意最后面的问号必须要加上，否则由于正则的贪婪匹配特性，footer的闭合标签会匹配失误。</p>
<p>OK了，<strong>匹配最多嵌套一级div</strong>的正则表达式如下：</p>
<blockquote><p>&lt;div [^&gt;]*id='footer'[^&gt;]*&gt;(<span style="color: #ff0000;">&lt;div[^&gt;]*&gt;.*?&lt;/div&gt;</span>|.)*?&lt;/div&gt;</p></blockquote>
<p>那么如果footer标签里头最多会嵌套两级div的话怎么办呢？</p>
<p>其实也不难，我们只需要把上面的“内容A”部分中的点号稍作替换即可。修改如下：</p>
<blockquote><p>&lt;div [^&gt;]*id='footer'[^&gt;]*&gt;(<span style="color: #ff0000;">&lt;div[^&gt;]*&gt;</span><span style="color: #0000ff;">(&lt;div[^&gt;]*&gt;.*?&lt;/div&gt;|.)</span>*?&lt;/div&gt;|.)*?&lt;/div&gt;</p></blockquote>
<p>到这里你可能就知道，如果要匹配最多嵌套三级div的话，正则应该怎么写了：</p>
<blockquote><p>&lt;div [^&gt;]*id='footer'[^&gt;]*&gt;(<span style="color: #ff0000;">&lt;div[^&gt;]*&gt;</span><span style="color: #0000ff;">(&lt;div[^&gt;]*&gt;</span><span style="color: #ff8000;">(&lt;div[^&gt;]*&gt;.*?&lt;/div&gt;|.)</span>*?&lt;/div&gt;|.)*?&lt;/div&gt;|.)*?&lt;/div&gt;</p></blockquote>
<p>所以实际上，只要你的html结构不是特别复杂的话，也就是说嵌套不会很深的话，那么你完全可以使用这种方式来匹配嵌套html标签。</p>
<p>这个正则在Java和Javascript中都可以使用，因为它没有用到任何高级特性。</p>
<p style="text-align: right;">——<a title="Java/Js如何使用正则表达式匹配嵌套Html标签" href="http://www.imkevinyang.com/2010/07/javajs%E5%A6%82%E4%BD%95%E4%BD%BF%E7%94%A8%E6%AD%A3%E5%88%99%E8%A1%A8%E8%BE%BE%E5%BC%8F%E5%8C%B9%E9%85%8D%E5%B5%8C%E5%A5%97html%E6%A0%87%E7%AD%BE.html" target="_self"><em>Kevin Yang</em></a></p>

	标签：<a href="http://www.imkevinyang.com/tags/html%e5%b5%8c%e5%a5%97%e6%a0%87%e7%ad%be%e5%8c%b9%e9%85%8d" title="Html嵌套标签匹配" rel="tag">Html嵌套标签匹配</a>, <a href="http://www.imkevinyang.com/tags/java" title="Java" rel="tag">Java</a>, <a href="http://www.imkevinyang.com/tags/javascript" title="Javascript" rel="tag">Javascript</a>, <a href="http://www.imkevinyang.com/categories/techarticles/othertecharticles" title="其他随笔" rel="tag">其他随笔</a>, <a href="http://www.imkevinyang.com/tags/%e6%ad%a3%e5%88%99%e8%a1%a8%e8%be%be%e5%bc%8f" title="正则表达式" rel="tag">正则表达式</a><br />

	<h4 style="background-color:#3B3B3B;border-bottom:2px groove gray;color:#F2F2F2;margin-top:20px;padding:6px 6px 6px 15px;margin:20px 0px 0px 0px">你可能对下面的文章感兴趣</h4>
	<ul class="st-related-posts">
	<li><a href="http://www.imkevinyang.com/2009/11/button%e6%a0%87%e7%ad%be%e9%bc%a0%e6%a0%87%e7%82%b9%e5%87%bb%e4%ba%8b%e4%bb%b6%e7%9a%84%e8%a7%a6%e5%8f%91%e6%ba%90%e9%97%ae%e9%a2%98.html" title="Button标签鼠标点击事件的触发源问题 (2009/11/27)">Button标签鼠标点击事件的触发源问题</a> </li>
	<li><a href="http://www.imkevinyang.com/2009/03/ie%e4%b8%ad%e4%bd%bf%e7%94%a8windowopen%e6%89%93%e5%bc%80%e6%96%b0%e7%aa%97%e5%8f%a3%e6%97%b6%e6%97%a0%e6%b3%95%e8%8e%b7%e5%8f%96referrer%e5%af%b9%e8%b1%a1.html" title="IE中使用window.open打开新窗口时无法获取Referrer对象 (2009/03/07)">IE中使用window.open打开新窗口时无法获取Referrer对象</a> </li>
	<li><a href="http://www.imkevinyang.com/2009/07/javascript-%e4%b8%ad%e7%9a%84false%e9%9b%b6%e5%80%bcnullundefined%e5%92%8c%e7%a9%ba%e5%ad%97%e7%ac%a6%e4%b8%b2%e5%af%b9%e8%b1%a1.html" title="Javascript 中的false,零值,null,undefined和空字符串对象 (2009/07/07)">Javascript 中的false,零值,null,undefined和空字符串对象</a> </li>
	<li><a href="http://www.imkevinyang.com/2009/05/javascript%e4%b8%ad%e8%8e%b7%e5%8f%96%e5%87%ba%e9%94%99%e4%bb%a3%e7%a0%81%e6%89%80%e5%9c%a8%e6%96%87%e4%bb%b6%e5%8f%8a%e8%a1%8c%e6%95%b0.html" title="Javascript中获取出错代码所在文件及行数 (2009/05/18)">Javascript中获取出错代码所在文件及行数</a> </li>
	<li><a href="http://www.imkevinyang.com/2009/04/javascript%e5%ad%97%e7%ac%a6%e4%b8%b2%e5%93%88%e5%b8%8c%e5%87%bd%e6%95%b0.html" title="Javascript字符串哈希函数 (2009/04/11)">Javascript字符串哈希函数</a> </li>
	<li><a href="http://www.imkevinyang.com/2009/06/javascript%e6%93%8d%e7%ba%b5cookie.html" title="Javascript操纵Cookie (2009/06/11)">Javascript操纵Cookie</a> </li>
	<li><a href="http://www.imkevinyang.com/2010/05/%e4%b8%ba%e4%bb%80%e4%b9%88iis77-5%e7%9a%84gzip%e4%b8%8d%e8%b5%b7%e4%bd%9c%e7%94%a8.html" title="为什么IIS7/7.5的Gzip不起作用 (2010/05/08)">为什么IIS7/7.5的Gzip不起作用</a> </li>
	<li><a href="http://www.imkevinyang.com/2009/07/%e4%bd%bf%e7%94%a8%e6%ad%a3%e5%88%99%e8%a1%a8%e8%be%be%e5%bc%8f%e5%8c%b9%e9%85%8d%e5%b5%8c%e5%a5%97html%e6%a0%87%e7%ad%be.html" title="使用正则表达式匹配嵌套Html标签 (2009/07/22)">使用正则表达式匹配嵌套Html标签</a> </li>
	<li><a href="http://www.imkevinyang.com/2009/08/%e4%bd%bf%e7%94%a8%e6%ad%a3%e5%88%99%e8%a1%a8%e8%be%be%e5%bc%8f%e6%89%be%e5%87%ba%e4%b8%8d%e5%8c%85%e5%90%ab%e7%89%b9%e5%ae%9a%e5%ad%97%e7%ac%a6%e4%b8%b2%e7%9a%84%e6%9d%a1%e7%9b%ae.html" title="使用正则表达式找出不包含特定字符串的条目 (2009/08/04)">使用正则表达式找出不包含特定字符串的条目</a> </li>
	<li><a href="http://www.imkevinyang.com/2009/09/%e4%bd%bf%e7%94%a8%e7%9b%b8%e5%af%b9url%e6%97%a0%e7%bc%9d%e5%88%87%e6%8d%a2http-https.html" title="使用相对Url无缝切换HTTP-HTTPS (2009/09/18)">使用相对Url无缝切换HTTP-HTTPS</a> </li>
</ul>

]]></content:encoded>
			<wfw:commentRss>http://www.imkevinyang.com/2010/07/javajs%e5%a6%82%e4%bd%95%e4%bd%bf%e7%94%a8%e6%ad%a3%e5%88%99%e8%a1%a8%e8%be%be%e5%bc%8f%e5%8c%b9%e9%85%8d%e5%b5%8c%e5%a5%97html%e6%a0%87%e7%ad%be.html/feed</wfw:commentRss>
		<slash:comments>11</slash:comments>
		</item>
		<item>
		<title>使用正则表达式找出不包含特定字符串的条目</title>
		<link>http://www.imkevinyang.com/2009/08/%e4%bd%bf%e7%94%a8%e6%ad%a3%e5%88%99%e8%a1%a8%e8%be%be%e5%bc%8f%e6%89%be%e5%87%ba%e4%b8%8d%e5%8c%85%e5%90%ab%e7%89%b9%e5%ae%9a%e5%ad%97%e7%ac%a6%e4%b8%b2%e7%9a%84%e6%9d%a1%e7%9b%ae.html</link>
		<comments>http://www.imkevinyang.com/2009/08/%e4%bd%bf%e7%94%a8%e6%ad%a3%e5%88%99%e8%a1%a8%e8%be%be%e5%bc%8f%e6%89%be%e5%87%ba%e4%b8%8d%e5%8c%85%e5%90%ab%e7%89%b9%e5%ae%9a%e5%ad%97%e7%ac%a6%e4%b8%b2%e7%9a%84%e6%9d%a1%e7%9b%ae.html#comments</comments>
		<pubDate>Mon, 03 Aug 2009 17:00:00 +0000</pubDate>
		<dc:creator>Kevin Yang</dc:creator>
				<category><![CDATA[技术随笔]]></category>
		<category><![CDATA[否定式前瞻]]></category>
		<category><![CDATA[字符串匹配]]></category>
		<category><![CDATA[排除特定字符串]]></category>
		<category><![CDATA[正则]]></category>
		<category><![CDATA[正则表达式]]></category>
		<category><![CDATA[肯定式前瞻]]></category>

		<guid isPermaLink="false">http://www.imkevinyang.com/2009/08/%e4%bd%bf%e7%94%a8%e6%ad%a3%e5%88%99%e8%a1%a8%e8%be%be%e5%bc%8f%e6%89%be%e5%87%ba%e4%b8%8d%e5%8c%85%e5%90%ab%e7%89%b9%e5%ae%9a%e5%ad%97%e7%ac%a6%e4%b8%b2%e7%9a%84%e6%9d%a1%e7%9b%ae.html</guid>
		<description><![CDATA[概述
<p>做日志分析工作的经常需要跟成千上万的日志条目打交道，为了在庞大的数据量中找到特定模式的数据，常常需要编写很多复杂的<a href="http://www.imkevinyang.com/2009/07/%e4%bd%bf%e7%94%a8%e6%ad%a3%e5%88%99%e8%a1%a8%e8%be%be%e5%bc%8f%e5%8c%b9%e9%85%8d%e5%b5%8c%e5%a5%97html%e6%a0%87%e7%ad%be.html" target="_blank">正则表达式</a>。例如枚举出日志文件中不包含某个特定字符串的条目，找出不以某个特定字符串打头的条目，等等。</p>
使用否定式前瞻
<p>正则表达式中有前瞻（Lookahead）和后顾（Lookbehind&#8230;</p>]]></description>
			<content:encoded><![CDATA[<h2>概述</h2>
<p>做日志分析工作的经常需要跟成千上万的日志条目打交道，为了在庞大的数据量中找到特定模式的数据，常常需要编写很多复杂的<a href="http://www.imkevinyang.com/2009/07/%e4%bd%bf%e7%94%a8%e6%ad%a3%e5%88%99%e8%a1%a8%e8%be%be%e5%bc%8f%e5%8c%b9%e9%85%8d%e5%b5%8c%e5%a5%97html%e6%a0%87%e7%ad%be.html" target="_blank">正则表达式</a>。例如枚举出日志文件中不包含某个特定字符串的条目，找出不以某个特定字符串打头的条目，等等。</p>
<h2>使用否定式前瞻</h2>
<p>正则表达式中有前瞻（Lookahead）和后顾（Lookbehind）的概念，这两个术语非常形象的描述了正则引擎的匹配行为。需要注意一点，正则表达式中的前和后和我们一般理解的前后有点不同。一段文本，我们一般习惯把文本开头的方向称作“前面”，文本末尾方向称为“后面”。但是<strong><span style="color: #008000;">对于正则表达式引擎来说，因为它是从文本头部向尾部开始解析的（可以通过正则选项控制解析方向），因此对于文本尾部方向，称为“前”，因为这个时候，正则引擎还没走到那块，而对文本头部方向，则称为“后”，因为正则引擎已经走过了那一块地方</span></strong>。如下图所示：</p>
<p><img style="margin: 0px; display: inline; border: 0px;" title="正向前瞻逆向前瞻" src="http://www.imkevinyang.com/wp-content/uploads/2009/08/image_thumb.png" border="0" alt="正向前瞻逆向前瞻" width="240" height="161" /></p>
<p>所谓的前瞻就是在正则表达式匹配到某个字符的时候，往“尚未解析过的文本”预先看一下，看是不是符合/不符合匹配模式，而后顾，就是在正则引擎已经匹配过的文本看看是不是符合/不符合匹配模式。符合和不符合特定匹配模式我们又称为<strong><span style="color: #008000;">肯定式匹配和否定式匹配</span></strong>。</p>
<p>现代高级正则表达式引擎一般都支持都支持前瞻，对于后顾支持并不是很广泛，因此我们这里采用否定式前瞻来实现我们的需求。</p>
<h2>实现</h2>
<p>测试数据：</p>
<pre class="csharpcode">2009-07-07 04:38:44 127.0.0.1 GET /robots.txt
2009-07-07 04:38:44 127.0.0.1 GET /posts/robotfile.txt
2009-07-08 04:38:44 127.0.0.1 GET /</pre>
<p>例如上面这几条简单的日志条目，我们想实现两个目标：</p>
<p>1. 把8号的数据过滤掉</p>
<p>2. 把那些不包含robots.txt字符串的条目给找出来（只要Url中包含robots.txt的都给过滤掉）。</p>
<p>前瞻的语法是：</p>
<pre class="csharpcode">(?!匹配模式)</pre>
<p>我们先来实现第一个目标——<strong><span style="color: #008000;">匹配不以特定字符串开头的条目</span></strong>。</p>
<p>这里我们因为要排除一段连续的字符串，因此匹配模式非常简单，就是2009-07-08。实现如下：</p>
<pre class="csharpcode">^(?!2009-07-08).*?$</pre>
<p>用<a title="Expresso正则表达式工具" href="http://www.imkevinyang.com/2009/07/%e3%80%90%e6%8e%a8%e8%8d%90%e3%80%91%e4%bd%bf%e7%94%a8ultrapico-expresso%e5%ad%a6%e4%b9%a0%e6%ad%a3%e5%88%99%e8%a1%a8%e8%be%be%e5%bc%8f.html" target="_blank">Expresso</a>我们可以看到结果确实过滤掉8号的数据。</p>
<p>接下来，我们来实现第二个目标——<strong><span style="color: #008000;">排除包含特定字符串的条目</span></strong>。</p>
<p>按照我们上面写法，我照葫芦画瓢了一下：</p>
<pre class="csharpcode">^.*?(?!robots\.txt).*?$</pre>
<p>这段正则用大白话描述就是：开头任意字符，然后后面不要跟着robots.txt连续字符串，然后再跟着任意个字符，字符串结尾。</p>
<p>运行测试，结果发现：</p>
<p><img style="margin: 0px; display: inline; border: 0px;" title="image" src="http://www.imkevinyang.com/wp-content/uploads/2009/08/image_thumb1.png" border="0" alt="image" width="360" height="83" /></p>
<p>没有达到我们想要的效果。这是为什么呢？我们给上面的正则表达式加上两个捕获分组调试一下：</p>
<pre class="csharpcode">^(.*?)(?!robots\.txt)(.*?)$</pre>
<p>测试结果：</p>
<p><img style="margin: 0px; display: inline; border: 0px;" title="image" src="http://www.imkevinyang.com/wp-content/uploads/2009/08/image_thumb2.png" border="0" alt="image" width="392" height="195" /></p>
<p>我们看到，第一个分组啥都没有匹配到，而第二个分组却匹配了整个字符串。再回过头来好好分析一下刚才那个正则表达式。实际上，当正则引擎解析到A区域的时候，就已经开始执行B区域的前瞻工作。这个时候发现当A区域为Null的时候匹配成功——.*本来就允许匹配空字符，前瞻条件又满足，A区域后面紧跟着的是“2009”字符串，而并不是robots。因此整个匹配过程成功匹配到所有条目。</p>
<p><img style="margin: 0px; display: inline; border: 0px;" title="image" src="http://www.imkevinyang.com/wp-content/uploads/2009/08/image_thumb3.png" border="0" alt="image" width="246" height="146" /></p>
<p>分析出原因之后我们对上述的正则进行修正，将.*?移入前瞻表达式，如下：</p>
<pre class="csharpcode">^(?!.*?robots).*$</pre>
<p>测试结果:</p>
<p><img style="margin: 0px; display: inline; border: 0px;" title="image" src="http://www.imkevinyang.com/wp-content/uploads/2009/08/image_thumb4.png" border="0" alt="image" width="360" height="72" /></p>
<p>Bingo!</p>
<p style="text-align: right;">——<a href="http://www.imkevinyang.com/"><em><strong>Kevin Yang</strong></em></a></p>

	标签：<a href="http://www.imkevinyang.com/tags/%e5%90%a6%e5%ae%9a%e5%bc%8f%e5%89%8d%e7%9e%bb" title="否定式前瞻" rel="tag">否定式前瞻</a>, <a href="http://www.imkevinyang.com/tags/%e5%ad%97%e7%ac%a6%e4%b8%b2%e5%8c%b9%e9%85%8d" title="字符串匹配" rel="tag">字符串匹配</a>, <a href="http://www.imkevinyang.com/categories/techarticles" title="技术随笔" rel="tag">技术随笔</a>, <a href="http://www.imkevinyang.com/tags/%e6%8e%92%e9%99%a4%e7%89%b9%e5%ae%9a%e5%ad%97%e7%ac%a6%e4%b8%b2" title="排除特定字符串" rel="tag">排除特定字符串</a>, <a href="http://www.imkevinyang.com/tags/%e6%ad%a3%e5%88%99" title="正则" rel="tag">正则</a>, <a href="http://www.imkevinyang.com/tags/%e6%ad%a3%e5%88%99%e8%a1%a8%e8%be%be%e5%bc%8f" title="正则表达式" rel="tag">正则表达式</a>, <a href="http://www.imkevinyang.com/tags/%e8%82%af%e5%ae%9a%e5%bc%8f%e5%89%8d%e7%9e%bb" title="肯定式前瞻" rel="tag">肯定式前瞻</a><br />

	<h4 style="background-color:#3B3B3B;border-bottom:2px groove gray;color:#F2F2F2;margin-top:20px;padding:6px 6px 6px 15px;margin:20px 0px 0px 0px">你可能对下面的文章感兴趣</h4>
	<ul class="st-related-posts">
	<li><a href="http://www.imkevinyang.com/2010/07/javajs%e5%a6%82%e4%bd%95%e4%bd%bf%e7%94%a8%e6%ad%a3%e5%88%99%e8%a1%a8%e8%be%be%e5%bc%8f%e5%8c%b9%e9%85%8d%e5%b5%8c%e5%a5%97html%e6%a0%87%e7%ad%be.html" title="Java/Js如何使用正则表达式匹配嵌套Html标签 (2010/07/30)">Java/Js如何使用正则表达式匹配嵌套Html标签</a> </li>
	<li><a href="http://www.imkevinyang.com/2009/09/visual-assist%e4%bb%a3%e7%a0%81%e6%8f%90%e7%a4%ba%e4%bd%bf%e7%94%a8%e7%9a%84%e5%ad%97%e7%ac%a6%e4%b8%b2%e6%a8%a1%e7%b3%8a%e5%8c%b9%e9%85%8d%e7%ae%97%e6%b3%95.html" title="Visual Assist代码提示使用的字符串模糊匹配算法 (2009/09/28)">Visual Assist代码提示使用的字符串模糊匹配算法</a> </li>
	<li><a href="http://www.imkevinyang.com/2009/07/%e4%bd%bf%e7%94%a8%e6%ad%a3%e5%88%99%e8%a1%a8%e8%be%be%e5%bc%8f%e5%8c%b9%e9%85%8d%e5%b5%8c%e5%a5%97html%e6%a0%87%e7%ad%be.html" title="使用正则表达式匹配嵌套Html标签 (2009/07/22)">使用正则表达式匹配嵌套Html标签</a> </li>
</ul>

]]></content:encoded>
			<wfw:commentRss>http://www.imkevinyang.com/2009/08/%e4%bd%bf%e7%94%a8%e6%ad%a3%e5%88%99%e8%a1%a8%e8%be%be%e5%bc%8f%e6%89%be%e5%87%ba%e4%b8%8d%e5%8c%85%e5%90%ab%e7%89%b9%e5%ae%9a%e5%ad%97%e7%ac%a6%e4%b8%b2%e7%9a%84%e6%9d%a1%e7%9b%ae.html/feed</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>使用正则表达式匹配嵌套Html标签</title>
		<link>http://www.imkevinyang.com/2009/07/%e4%bd%bf%e7%94%a8%e6%ad%a3%e5%88%99%e8%a1%a8%e8%be%be%e5%bc%8f%e5%8c%b9%e9%85%8d%e5%b5%8c%e5%a5%97html%e6%a0%87%e7%ad%be.html</link>
		<comments>http://www.imkevinyang.com/2009/07/%e4%bd%bf%e7%94%a8%e6%ad%a3%e5%88%99%e8%a1%a8%e8%be%be%e5%bc%8f%e5%8c%b9%e9%85%8d%e5%b5%8c%e5%a5%97html%e6%a0%87%e7%ad%be.html#comments</comments>
		<pubDate>Wed, 22 Jul 2009 01:20:00 +0000</pubDate>
		<dc:creator>Kevin Yang</dc:creator>
				<category><![CDATA[技术随笔]]></category>
		<category><![CDATA[Html标签匹配]]></category>
		<category><![CDATA[常用正则表达式]]></category>
		<category><![CDATA[正则表达式]]></category>
		<category><![CDATA[高级正则]]></category>

		<guid isPermaLink="false">http://www.imkevinyang.com/2009/07/%e4%bd%bf%e7%94%a8%e6%ad%a3%e5%88%99%e8%a1%a8%e8%be%be%e5%bc%8f%e5%8c%b9%e9%85%8d%e5%b5%8c%e5%a5%97html%e6%a0%87%e7%ad%be.html</guid>
		<description><![CDATA[概述
<p>正则表达式是做文本解析工作必不可少的技能。如Web服务器日志分析，网页前端开发等。很多高级文本编辑器都支持正则表达式的一个子集，熟练掌握正则表达式，经常能够使你的一些工作事半功倍。例如<a href="http://www.imkevinyang.com/2009/05/visual-studio%e7%bb%9f%e8%ae%a1%e6%9c%89%e6%95%88%e4%bb%a3%e7%a0%81%e8%a1%8c%e6%95%b0.html" target="_blank">统计代码行数</a>，只需一个正则就搞定。嵌套Html标签的匹配是正则表达式应用中一个比较难的话题，因为它涉及到的正则语&#8230;</p>]]></description>
			<content:encoded><![CDATA[<h2>概述</h2>
<p>正则表达式是做文本解析工作必不可少的技能。如Web服务器日志分析，网页前端开发等。很多高级文本编辑器都支持正则表达式的一个子集，熟练掌握正则表达式，经常能够使你的一些工作事半功倍。例如<a href="http://www.imkevinyang.com/2009/05/visual-studio%e7%bb%9f%e8%ae%a1%e6%9c%89%e6%95%88%e4%bb%a3%e7%a0%81%e8%a1%8c%e6%95%b0.html" target="_blank">统计代码行数</a>，只需一个正则就搞定。嵌套Html标签的匹配是正则表达式应用中一个比较难的话题，因为它涉及到的正则语法比较多，也比较难。因此也就更有研究的价值。</p>
<h2>思路</h2>
<p>任何复杂的正则表达式都是由简单的子表达式组成的，要想写出复杂的正则来，一方面需要有化繁为简的功底，另外一方面，我们需要从正则引擎的角度去思考问题。关于正则引擎的原理，推荐《Mastering Regular Expression》中文名叫《精通正则表达式》。挺不错的一本书。</p>
<p>OK，先确定我们要解决的问题——<strong><span style="color: #008000;">从一段Html文本中找出特定id的标签的innerHTML</span></strong>。</p>
<p>这里面最大的难点就是，Html标签是支持嵌套的，怎么能够找到指定标签相对应的闭合标签呢？</p>
<p>我们可以这样想，<strong><span style="color: #008000;">先匹配最前面的起始标签，假设是div吧（&lt;div），接着一旦遇到嵌套div，就“压入堆栈”，后面如果遇到div闭合标签了，就“弹出堆栈”。如果遇到闭合标签的时候，堆栈里面已经没有东西了，那么匹配结束，此结束标签为正确的闭合标签</span></strong>。</p>
<p>我之所以能够这样去思考，是因为我了解过正则的特性，我知道正则中的平衡组能够实现我刚才说的“堆栈”操作。所以，如果我们要编写复杂正则表达式，需要对正则的一些高级特性至少有所了解，这样我们思考问题才有个方向。</p>
<h2>实现</h2>
<p>这里假设我们要匹配的文本是一段合法的Html文本。下面这段Html代码是从我的博客上拷贝下来的，作为我们的测试文本。我们要匹配的就是footer这个div的innerHTML，同时把标签名也捕获下来。</p>
<pre class="csharpcode"><span class="kwrd">&lt;</span><span class="html">div</span> <span class="attr">style</span><span class="kwrd">="background-color:gray;"</span> <span class="attr">id</span><span class="kwrd">="footer"</span><span class="kwrd">&gt;</span>
    <span class="kwrd">&lt;</span><span class="html">a</span> <span class="attr">id</span><span class="kwrd">="gotop"</span> <span class="attr">href</span><span class="kwrd">="#"</span> <span class="attr">onclick</span><span class="kwrd">="MGJS.goTop();return false;"</span><span class="kwrd">&gt;</span>Top<span class="kwrd">&lt;/</span><span class="html">a</span><span class="kwrd">&gt;</span>
    <span class="kwrd">&lt;</span><span class="html">a</span> <span class="attr">id</span><span class="kwrd">="powered"</span> <span class="attr">href</span><span class="kwrd">="http://wordpress.org/"</span><span class="kwrd">&gt;</span>WordPress<span class="kwrd">&lt;/</span><span class="html">a</span><span class="kwrd">&gt;</span>
    <span class="kwrd">&lt;</span><span class="html">div</span> <span class="attr">id</span><span class="kwrd">="copyright"</span><span class="kwrd">&gt;</span>
        Copyright <span class="attr">&amp;copy;</span> 2009 简单生活 —— Kevin Yang的博客
    <span class="kwrd">&lt;/</span><span class="html">div</span><span class="kwrd">&gt;</span>
    <span class="kwrd">&lt;</span><span class="html">div</span> <span class="attr">id</span><span class="kwrd">="themeinfo"</span><span class="kwrd">&gt;</span>
        Theme by <span class="kwrd">&lt;</span><span class="html">a</span> <span class="attr">href</span><span class="kwrd">="http://www.neoease.com/"</span><span class="kwrd">&gt;</span>mg12<span class="kwrd">&lt;/</span><span class="html">a</span><span class="kwrd">&gt;</span>. Valid <span class="kwrd">&lt;</span><span class="html">a</span> <span class="attr">href</span><span class="kwrd">="http://validator.w3.org/check?uri=referer"</span><span class="kwrd">&gt;</span>XHTML 1.1<span class="kwrd">&lt;/</span><span class="html">a</span><span class="kwrd">&gt;</span>
        and <span class="kwrd">&lt;</span><span class="html">a</span> <span class="attr">href</span><span class="kwrd">="http://jigsaw.w3.org/css-validator/"</span><span class="kwrd">&gt;</span>CSS 3<span class="kwrd">&lt;/</span><span class="html">a</span><span class="kwrd">&gt;</span>.
    <span class="kwrd">&lt;/</span><span class="html">div</span><span class="kwrd">&gt;</span>
<span class="kwrd">&lt;/</span><span class="html">div</span><span class="kwrd">&gt;</span></pre>
<p>这里我们需要借助<a href="http://www.imkevinyang.com/2009/07/%e3%80%90%e6%8e%a8%e8%8d%90%e3%80%91%e4%bd%bf%e7%94%a8ultrapico-expresso%e5%ad%a6%e4%b9%a0%e6%ad%a3%e5%88%99%e8%a1%a8%e8%be%be%e5%bc%8f.html" target="_blank">Expresso</a>工具来构建和测试编写的正则表达式。</p>
<h2>匹配起始标签</h2>
<p>起始标签特征很好提取，以尖括号打头，然后跟着一连串英文字母，然后一大串属性中（非尖括号字符）匹配id（不区分大小写）=footer。需要注意的是，footer可以被双引号或者单引号包裹，也可以什么都不加。正则如下：</p>
<pre class="csharpcode">&lt;(?&lt;HtmlTag&gt;[\w]+)[^&gt;]*\s[iI][dD]=(?&lt;Quote&gt;["']?)footer(?(Quote)\k&lt;Quote&gt;)["']?[^&gt;]*&gt;</pre>
<p>上面的正则表达式需要做几点说明：</p>
<p>1. &lt;尖括号在正则中算是一个特殊字符，在显式捕获分组中用它将分组名括起来。但是因为开头的尖括号在此上下文下并不会出现解析歧义，因此加不加转义符效果是一样的。</p>
<p>2. (?&lt;GroupName&gt;RegEx)格式定义一个命名分组，我们在上面定义了一个HtmlTag的标签分组，用来存放匹配到的Html标签名。Quote分组是用来给后面的匹配使用的。</p>
<p>3. (?(GroupName)Then|Else)是条件语句，表示当捕获到GroupName分组时执行Then匹配，否则执行Else匹配。上面的正则中，我们先尝试匹配footer字符串左边的引号，并将其存入LeftQuote分组中，然后在footer右侧进行条件解析，如果之前匹配到LeftQuote分组，那么右侧也应该匹配LeftQuote分组。这样一来，我们就能精确匹配id的各种情况了。</p>
<h2>匹配闭合标签</h2>
<pre class="csharpcode">((?&lt;Nested&gt;&lt;\k&lt;HtmlTag&gt;[^&gt;]*&gt;)|&lt;/\k&lt;HtmlTag&gt;&gt;(?&lt;-Nested&gt;)|.*?)*&lt;/\k&lt;HtmlTag&gt;&gt;</pre>
<p>在成功匹配到起始标签之后，后面的Html文本可以分为三种情况：</p>
<p>A. 匹配到嵌套div起始标签&lt;div，这个时候，需要将其捕获到Nested分组。</p>
<p>B. 匹配到嵌套div起始标签的闭合标签，这个时候，需要将之前的Nested分组释放</p>
<p>C. 其他任意文本。注意，需要使用.*?方式关闭贪婪匹配，否则最后的闭合标签可能会过度匹配</p>
<p>使用(RegEx1|RegEx2|RegEx3)*这种方式，可以将几个条件以或的形式组合起来，然后再取若干次匹配结果，最终再匹配闭合标签。其中(?&lt;-Nested&gt;)是表示释放之前捕获的Nested分组。确切的语法是(?&lt;N-M&gt;)即使用N分组替换掉M分组，如果N分组没有指定或不存在，则释放M分组。</p>
<p><span style="color: #ff0000; font-size: medium;">update</span>：前面过于侧重分析了，最后没有给出一个完整的正则真是抱歉。</p>
<pre class="csharpcode">&lt;(?&lt;HtmlTag&gt;[\w]+)[^&gt;]*\s[iI][dD]=(?&lt;Quote&gt;[<span class="str">"']?)footer(?(Quote)\k&lt;Quote&gt;)["</span>']?[^&gt;]*&gt;((?&lt;Nested&gt;&lt;\k&lt;HtmlTag&gt;[^&gt;]*&gt;)|&lt;/\k&lt;HtmlTag&gt;&gt;(?&lt;-Nested&gt;)|.*?)*&lt;/\k&lt;HtmlTag&gt;&gt;</pre>
<p>上面这个正则能够匹配任意id=footer的html标签。</p>
<p>需要注意，此正则表达式需要设置SingleLine=true，这样点号才可以把换行符也匹配进去。</p>
<p>对于domoxz 的问题，如果要匹配p标签，那么只需将上述的正则中的&lt;(?&lt;HtmlTag&gt;[\w]+)替换成&lt;(?&lt;HtmlTag&gt;p)即可。</p>
<p style="text-align: right;">——<a href="http://www.imkevinyang.com/"><em><strong>Kevin Yang</strong></em></a></p>

	标签：<a href="http://www.imkevinyang.com/tags/html%e6%a0%87%e7%ad%be%e5%8c%b9%e9%85%8d" title="Html标签匹配" rel="tag">Html标签匹配</a>, <a href="http://www.imkevinyang.com/tags/%e5%b8%b8%e7%94%a8%e6%ad%a3%e5%88%99%e8%a1%a8%e8%be%be%e5%bc%8f" title="常用正则表达式" rel="tag">常用正则表达式</a>, <a href="http://www.imkevinyang.com/categories/techarticles" title="技术随笔" rel="tag">技术随笔</a>, <a href="http://www.imkevinyang.com/tags/%e6%ad%a3%e5%88%99%e8%a1%a8%e8%be%be%e5%bc%8f" title="正则表达式" rel="tag">正则表达式</a>, <a href="http://www.imkevinyang.com/tags/%e9%ab%98%e7%ba%a7%e6%ad%a3%e5%88%99" title="高级正则" rel="tag">高级正则</a><br />

	<h4 style="background-color:#3B3B3B;border-bottom:2px groove gray;color:#F2F2F2;margin-top:20px;padding:6px 6px 6px 15px;margin:20px 0px 0px 0px">你可能对下面的文章感兴趣</h4>
	<ul class="st-related-posts">
	<li><a href="http://www.imkevinyang.com/2010/07/javajs%e5%a6%82%e4%bd%95%e4%bd%bf%e7%94%a8%e6%ad%a3%e5%88%99%e8%a1%a8%e8%be%be%e5%bc%8f%e5%8c%b9%e9%85%8d%e5%b5%8c%e5%a5%97html%e6%a0%87%e7%ad%be.html" title="Java/Js如何使用正则表达式匹配嵌套Html标签 (2010/07/30)">Java/Js如何使用正则表达式匹配嵌套Html标签</a> </li>
	<li><a href="http://www.imkevinyang.com/2009/08/%e4%bd%bf%e7%94%a8%e6%ad%a3%e5%88%99%e8%a1%a8%e8%be%be%e5%bc%8f%e6%89%be%e5%87%ba%e4%b8%8d%e5%8c%85%e5%90%ab%e7%89%b9%e5%ae%9a%e5%ad%97%e7%ac%a6%e4%b8%b2%e7%9a%84%e6%9d%a1%e7%9b%ae.html" title="使用正则表达式找出不包含特定字符串的条目 (2009/08/04)">使用正则表达式找出不包含特定字符串的条目</a> </li>
</ul>

]]></content:encoded>
			<wfw:commentRss>http://www.imkevinyang.com/2009/07/%e4%bd%bf%e7%94%a8%e6%ad%a3%e5%88%99%e8%a1%a8%e8%be%be%e5%bc%8f%e5%8c%b9%e9%85%8d%e5%b5%8c%e5%a5%97html%e6%a0%87%e7%ad%be.html/feed</wfw:commentRss>
		<slash:comments>30</slash:comments>
		</item>
	</channel>
</rss>

<!-- Performance optimized by W3 Total Cache. Learn more: http://www.w3-edge.com/wordpress-plugins/

Page Caching using disk: enhanced

Served from: www.imkevinyang.com @ 2012-02-09 10:45:08 -->
