.NET正则基础之.NET正则匹配模式
<div id="navCategory"><h5 class="catalogue">目录</h5><ul class="first_class_ul"><li><a href="#_label0">1、概述</a></li><li><a href="#_label1">2、匹配模式</a></li><ul class="second_class_ul"><li><a href="#_lab2_1_0">2.1全局匹配模式和内联匹配模式概述</a></li><li><a href="#_lab2_1_1">2.2全局匹配模式</a></li><ul class="third_class_ul"><li><a href="#_label3_1_1_0">2.2.1 IgnoreCase 忽略大小写</a></li><li><a href="#_label3_1_1_1">2.2.2 Multiline 多行模式</a></li></ul><li><a href="#_lab2_1_2">2.2.2.1 在不开启多行模式情况下,“^”和“$”匹配范围</a></li><ul class="third_class_ul"></ul><li><a href="#_lab2_1_3">2.2.2.2 在开启了多行模式后,“^”和“$”匹配范围</a></li><ul class="third_class_ul"></ul><li><a href="#_lab2_1_4">2.2.2.3 匹配结果分析</a></li><ul class="third_class_ul"></ul><li><a href="#_lab2_1_5">2.2.2.4 应用举例</a></li><ul class="third_class_ul"></ul><li><a href="#_lab2_1_6">2.2.3 Singleline 单行模式</a></li><ul class="third_class_ul"></ul><li><a href="#_lab2_1_7">2.2.4 Compiled 编译模式</a></li><ul class="third_class_ul"></ul><li><a href="#_lab2_1_8">2.2.5 RightToLeft 从右到左模式</a></li><ul class="third_class_ul"></ul><li><a href="#_lab2_1_9">2.2.6 None</a></li><ul class="third_class_ul"></ul><li><a href="#_lab2_1_10">2.2.7 ExplicitCapture</a></li><ul class="third_class_ul"></ul><li><a href="#_lab2_1_11">2.2.8 IgnorePatternWhitespace</a></li><ul class="third_class_ul"></ul><li><a href="#_lab2_1_12">2.2.9 ECMAScript</a></li><ul class="third_class_ul"></ul><li><a href="#_lab2_1_13">2.2.10 CultureInvariant</a></li><ul class="third_class_ul"></ul><li><a href="#_lab2_1_14">2.3 内联匹配模式</a></li><ul class="third_class_ul"><li><a href="#_label3_1_14_2">2.3.1 (?imnsx-imnsx:)形式</a></li><li><a href="#_label3_1_14_3">2.3.2 (?imnsx-imnsx)</a></li></ul></ul><li><a href="#_label2">3、小结</a></li><ul class="second_class_ul"></ul></ul></div><p class="maodian"><a name="_label0"></a></p><h2>1、概述</h2><p>匹配模式指的是一些可以改变正则表达式匹配行为的选项或修饰符。不同的语言支持的匹配模式不同,使用的方式也不同。</p>
<p>一般可分为全局和内联两种。下面主要介绍.NET中的匹配模式,其它语言的匹配模式,后续视情况补充。</p>
<p class="maodian"><a name="_label1"></a></p><h2>2、匹配模式</h2>
<p class="maodian"><a name="_lab2_1_0"></a></p><h3>2.1全局匹配模式和内联匹配模式概述</h3>
<p>.NET中的全局匹配模式是通过RegexOptions 枚举值指定的,可以按位“或”进行组合,全局匹配模式只能作用于整个正则表达式。全局匹配模式提供的模式更多,可以灵活的动态指定匹配模式。</p>
<p>而内联匹配模式是通过在正则表达式中,使用(?imnsx-imnsx:)或(?imnsx-imnsx)的分组构造来改变正则表达式的匹配行为的。内联匹配模式更灵活、简洁,但是提供的模式较少。</p>
<p>下表摘自MSDN,部分说明做了修改。</p>
<table><tbody><tr><td><p>RegexOption 成员</p></td><td><p>内联字符</p></td><td><p>说明</p></td></tr><tr><td><p>None</p></td><td><p>N/A</p></td><td><p>指定不设置任何选项。</p></td></tr><tr><td><p>IgnoreCase</p></td><td><p>i</p></td><td><p>指定不区分大小写的匹配。</p></td></tr><tr><td><p>Multiline</p></td><td><p>m</p></td><td><p>指定多行模式。更改 ^ 和 $ 的含义,以使它们分别与任何行的开头和结尾匹配,而不只是与整个字符串的开头和结尾匹配。</p></td></tr><tr><td><p>ExplicitCapture</p></td><td><p>n</p></td><td><p>指定唯一有效的捕获是显式命名或编号的 (?<name>…) 形式的组。这允许圆括号充当非捕获组,从而避免了由 (?:…) 导致的语法上的笨拙。</p></td></tr><tr><td><p>Compiled</p></td><td><p>N/A</p></td><td><p>指定正则表达式将被编译为程序集。生成该正则表达式的 Microsoft 中间语言 (MSIL) 代码;以较长的启动时间为代价,得到更快的执行速度。</p></td></tr><tr><td><p>Singleline</p></td><td><p>s</p></td><td><p>指定单行模式。更改句点字符 (.) 的含义,以使它与每个字符(而不是除 \n 之外的所有字符)匹配。</p></td></tr><tr><td><p>IgnorePatternWhitespace</p></td><td><p>x</p></td><td><p>指定从模式中排除非转义空白并启用数字符号 (#) 后面的注释。请注意,空白永远不会从字符类中消除。</p></td></tr><tr><td><p>RightToLeft</p></td><td><p>N/A</p></td><td><p>指定搜索是从右向左而不是从左向右进行的。具有此选项的正则表达式将移动到起始位置的左边而不是右边。</p></td></tr><tr><td><p>ECMAScript</p></td><td><p>N/A</p></td><td><p>指定已为表达式启用了符合 ECMAScript 的行为。此选项仅可与 IgnoreCase 和 Multiline 标志一起使用。将 ECMAScript 同任何其他标志一起使用将导致异常。</p></td></tr><tr><td><p>CultureInvariant</p></td><td><p>N/A</p></td><td><p>指定忽略语言中的区域性差异。</p></td></tr></tbody></table>
<p class="maodian"><a name="_lab2_1_1"></a></p><h3>2.2全局匹配模式</h3>
<p>下面根据各种模式使用的频率进行讲解。</p>
<p class="maodian"><a name="_label3_1_1_0"></a></p><h4>2.2.1 IgnoreCase 忽略大小写</h4>
<p>几乎所有支持正则的语言中都提供了这一模式,是应用最多的模式之一,同时也是被“滥”用最多的模式之一。</p>
<p>开启忽略大小写模式,则字符可以同时匹配大写或小写形式。比如在此模式下,正则表达式“<br>”可同时匹配“<br>”和“<BR>”</p>
<p>但并不是所有的字符都有大小写形式,所以在书写的正则中,如果不存在可区分大小写形式的元字符或是字符序列时,开启这一模式是没有任何意义的。</p>
<p>比如替换一般的html标签的正则表达式</p>
<p>string result = Regex.Replace(srcStr, @"<[^>]*>", "", RegexOptions.IgnoreCase);</p>
<p>因为<[^>]*>没有哪一个元字符或是字符序列具有大小写形式,所以这里的RegexOptions.IgnoreCase是多余的,用在这里虽然不会改变匹配结果,但是会降低匹配效率,同时这也不是一个好的习惯。</p>
<p>只有在正则表达式中,注意是正则表达式中,而不是待匹配的源字符串中,涉及到大小写形式的元字符或是字符序列时,才使用IgnoreCase模式。</p>
<p class="maodian"><a name="_label3_1_1_1"></a></p><h4>2.2.2 Multiline 多行模式</h4>
<p>多行模式改变的是“^”和“$”的匹配行为,使“^”和“$”不仅可以匹配整个字符串的开始和结束位置,还可以匹配每行的开始和结束位置。</p>
<p>首先说明一下“行”的范围。虽然我们在程序中习惯用“\r\n”来表示换行,但实际上“\r”和“\n”是不相关的两个字符,一个表示回车,一个表示换行。由于历史原因,“\r”并不是所有系统都支持的,所以“行”是由“\n”来分割的,其中“\n”属于前一“行”,而不属于后一“行”。</p>
<p>举例来说,字符串“a\r\nbc\r\n”共有三行,“a\r\n”为一行,“bc\r\n”为一行,最后还有一个“”空行。</p>
<p class="maodian"><a name="_lab2_1_2"></a></p><h3>2.2.2.1 在不开启多行模式情况下,“^”和“$”匹配范围</h3>
<p>“^”的匹配范围</p>
<div class="jb51code"><pre class="brush:csharp;">MatchCollection mc = Regex.Matches("a\r\nbc\r\n", @"^");
foreach (Match m in mc)
{
richTextBox2.Text += "匹配内容:" + m.Value + " 匹配开始索引:" + m.Index + " 匹配长度:" + m.Length + "\n";
}
/*--------输出--------
匹配内容: 匹配开始索引:0 匹配长度:0
*/</pre></div>
<p>“$”的匹配范围</p>
<div class="jb51code"><pre class="brush:csharp;">MatchCollection mc = Regex.Matches("a\r\nbc\r\n", @"$");
foreach (Match m in mc)
{
richTextBox2.Text += "匹配内容:" + m.Value + " 匹配开始索引:" + m.Index + " 匹配长度:" + m.Length + "\n";
}
/*--------输出--------
匹配内容: 匹配开始索引:6 匹配长度:0
匹配内容: 匹配开始索引:7 匹配长度:0
*/</pre></div>
<p>注意:这里需要注意的是,在没有开启多行模式时,字符串结尾如果是“\n”,那么“$”是可以匹配两个位置的,一个是“\n”前的位置,一个是字符串结束位置。字符串结尾如果不是“\n”,那么“$”就只匹配字符串结束位置。</p>
<div class="jb51code"><pre class="brush:csharp;">MatchCollection mc = Regex.Matches("a\r\nbc\r", @"$");
foreach (Match m in mc)
{
richTextBox2.Text += "匹配内容:" + m.Value + " 匹配开始索引:" + m.Index + " 匹配长度:" + m.Length + "\n";
}
/*--------输出--------
匹配内容: 匹配开始索引:6 匹配长度:0
*/</pre></div>
<p class="maodian"><a name="_lab2_1_3"></a></p><h3>2.2.2.2 在开启了多行模式后,“^”和“$”匹配范围</h3>
<p>“^”的匹配范围</p>
<div class="jb51code"><pre class="brush:csharp;">MatchCollection mc = Regex.Matches("a\r\nbc\r\n", @"^", RegexOptions.Multiline);
foreach (Match m in mc)
{
richTextBox2.Text += "匹配内容:" + m.Value + " 匹配开始索引:" + m.Index + " 匹配长度:" + m.Length + "\n";
}
/*--------输出--------
匹配内容: 匹配开始索引:0 匹配长度:0
匹配内容: 匹配开始索引:3 匹配长度:0
匹配内容: 匹配开始索引:7 匹配长度:0
*/</pre></div>
<p>“$”的匹配范围</p>
<div class="jb51code"><pre class="brush:csharp;">MatchCollection mc = Regex.Matches("a\r\nbc\r\n", @"$", RegexOptions.Multiline);
foreach (Match m in mc)
{
richTextBox2.Text += "匹配内容:" + m.Value + " 匹配开始索引:" + m.Index + " 匹配长度:" + m.Length + "\n";
}
/*--------输出--------
匹配内容: 匹配开始索引:2 匹配长度:0
匹配内容: 匹配开始索引:6 匹配长度:0
匹配内容: 匹配开始索引:7 匹配长度:0
*/</pre></div>
<p class="maodian"><a name="_lab2_1_4"></a></p><h3>2.2.2.3 匹配结果分析</h3>
<p>“^”匹配结果分析</p>
<p>在不开启多行模式时,“^”只匹配字符串的开始位置,也就是位置0。</p>
<p>在开启了多行模式后,“^”匹配字符串开始位置和每个“\n”之后的行起始位置。</p>
<p>“$”匹配结果分析</p>
<p>在不开启多行模式时,如果字符结尾是“\n”,那么“$”会匹配结尾“\n”之前和结束两个位置。</p>
<p>在开启多行模式后,“$”匹配每行“\n”之前的位置和字符串结束位置。</p>
<p>需要注意的是,在.NET中,无论是否开启多行模式,“^”和“$”匹配的都只是一个位置,是零宽度的。其它语言中“^”和“$”的意义可能会有所不同。</p>
<p>只有在正则表达式中涉及到多行的“^”和“$”的匹配时,才使用Multiline模式。</p>
<p class="maodian"><a name="_lab2_1_5"></a></p><h3>2.2.2.4 应用举例</h3>
<p>典型应用一(参考 <a href="http://topic.csdn.net/u/20090531/07/ff1c1d9a-7abe-4703-bdb4-8cd86328b871.html" rel="external nofollow" target="_blank">正则中加字符的问题</a>):</p>
<p>需求描述:</p>
<p>fffffffffff</p>
<p>fffffffffff</p>
<p>dfdfdfdf</p>
<p>erererere</p>
<p>ferewfdfds</p>
<p>每行后面加一个“ttt”,即为</p>
<p>fffffffffffttt</p>
<p>fffffffffffttt</p>
<p>dfdfdfdfttt</p>
<p>ererererettt</p>
<p>ferewfdfdsttt</p>
<p>代码实现:</p>
<blockquote><p>string result = Regex.Replace(yourStr, @"^.*$", "$0ttt", RegexOptions.Multiline);</p></blockquote>
<p>典型应用二</p>
<p>需求描述:</p>
<p>源字符串</p>
<p>CODE39/CODE93:</p>
<p>A-Z</p>
<p>space,-,.,$,/,+,%</p>
<p>CODE128A:</p>
<p>A-Z</p>
<p>0-9</p>
<p>space,!,",#,$,%,&,…,(,)*,+,逗号,-,.,/, <,=,>,?,@,[,],^,_</p>
<p>CODE128B:</p>
<p>A-Z</p>
<p>a-z</p>
<p>0-9</p>
<p>space,!,",#,$,%,&,…,(,)*,+,逗号,-,.,/, <,=,>,?,@,[,],^,_,{,},|,~</p>
<p>CODE2of5:</p>
<p>0-9</p>
<p>说明:</p>
<p>CODE128A:->条码类别</p>
<p>A-Z</p>
<p>0-9</p>
<p>space,!,",#,$,%,&,…,(,)*,+,逗号,-,.,/, <,=,>,?,@,[,],^,_->表示范围</p>
<p>要求分别匹配出条码类别和表示范围</p>
<p>代码实现:</p>
<div class="jb51code"><pre class="brush:csharp;">MatchCollection mc = Regex.Matches(yourStr, @"(?<type>[^\n:]+):\s*(?<range>(^(?!\s*$).*$\n?)*)", RegexOptions.Multiline);
foreach (Match m in mc)
{
richTextBox2.Text += "条码类别: \n" + m.Groups["type"].Value + "\n";
richTextBox2.Text += "表示范围: \n" + m.Groups["range"].Value + "\n";
}
/*--------输出--------
条码类别:
CODE39/CODE93
表示范围:
A-Z
space,-,.,$,/,+,%
条码类别:
CODE128A
表示范围:
A-Z
0-9
space,!,",#,$,%,&,…,(,)*,+,逗号,-,.,/, <,=,>,?,@,[,],^,_
条码类别:
CODE128B
表示范围:
A-Z
a-z
0-9
space,!,",#,$,%,&,…,(,)*,+,逗号,-,.,/, <,=,>,?,@,[,],^,_,{,},|,~
条码类别:
CODE2of5
表示范围:
0-9
*/</pre></div>
<p class="maodian"><a name="_lab2_1_6"></a></p><h3>2.2.3 Singleline 单行模式</h3>
<p>单行模式改变的是小数点“.”的匹配行为,使小数点可以匹配包含换行符“\n”在内的任意一个字符。</p>
<p>这是一个很不幸的命名,总让人误会它与Multiline多行模式是有关联的,而事实上它改变的是小数点的匹配行为,与Multiline多行模式没有任何关联,由于历史原因,这一不幸的命名被保留了下来。使用时需要注意。</p>
<p>单行模式通常在匹配有换行的文本时使用,采用小数点+单行模式的方式匹配任意字符,在.NET中是效率最高的。参考 <a href="http://blog.csdn.net/lxcnn/archive/2009/06/20/4284817.aspx" rel="external nofollow" target="_blank">正则基础之——小数点</a>。</p>
<p>典型应用:</p>
<p>源字符串:</p>
<div class="jb51code"><pre class="brush:xhtml;"><a>There is one obvious advantage:</a>
<div>
<p>
You've seen it coming!<br/>
Buy now and get nothing for free!<br/>
Well, at least no free beer. Perhaps a bear,<br/>
if you can afford it.
</p>
</div>
<a>Now that you've got...</a>
<div>
<p>
your bear, you have to admit it!<br/>
No, we aren't selling bears.
</p>
</div></pre></div>
<p>需求:取<div>标签之间的内容</p>
<p>代码实现:</p>
<div class="jb51code"><pre class="brush:csharp;">MatchCollection mc = Regex.Matches(yourStr, @"<div[^>]*>(?:(?!</div\b).)*</div>", RegexOptions.Singleline|RegexOptions.IgnoreCase);
foreach (Match m in mc)
{
richTextBox2.Text += m.Value + "\n-------------------\n";
}
/*--------输出--------
<div>
<p>
You've seen it coming!<br/>
Buy now and get nothing for free!<br/>
Well, at least no free beer. Perhaps a bear,<br/>
if you can afford it.
</p>
</div>
-------------------
<div>
<p>
your bear, you have to admit it!<br/>
No, we aren't selling bears.
</p>
</div>
-------------------
*/</pre></div>
<p class="maodian"><a name="_lab2_1_7"></a></p><h3>2.2.4 Compiled 编译模式</h3>
<p>Compiled改变的是.NET中正则表达式的编译方式。启用了Compiled模式,会延长启动时间,占用更多的内存,会提高匹配速度。当然,对最终性能的影响,需要根据具体问题综合考虑的。这一模式也是被“滥”用最多的模式之一。</p>
<p>程序运行过程中,第一次遇到正则表达式,需要加载正则引擎,对正则表达式进行必要的语法检查,并做适当的优化,最后把它转换为适合正则引擎应用的形式。这种“解析”过程,对于复杂的正则表达式,频繁调用或是匹配较大的数据源时,对效率的影响较大。</p>
<p>这时可以在构建正则表达式时开启Compiled模式。这样做会将正则表达式直接编译为MSIL代码,在正则匹配过程中,可以由JIT优化为更快的本地机器代码,获得更高的匹配速度。但这种方式会降低正则的解析速度,占用更多的内存,而且它占用的内存在程序运行过程中会一直占用,无法释放。</p>
<p>什么场景下使用Compiled模式,需要根据实际情况具体问题具体分析,一般来说,以下场景不适合使用Compiled模式:</p>
<p>1. 对匹配效率没有要求的场景;</p>
<p>2. 非常简单的正则表达式;</p>
<p>3. 极少调用的方法中声明的正则表达式;</p>
<p>4. 循环体中声明的正则表达式(除了动态生成的正则表达式,否则不要在循环体内声明正则表达式);</p>
<p>5. 静态方法中声明的正则表达式(静态方法每次调用都需要重新编辑正则表达式,使用Compiled模式只会降低效率)。</p>
<p class="maodian"><a name="_lab2_1_8"></a></p><h3>2.2.5 RightToLeft 从右到左模式</h3>
<p>RightToLeft改变的是正则表达式匹配的顺序,从右到左进行匹配。目前只有.NET支持这一模式,但它对这一模式的支持并不是很完善,有时容易让人费解,所以除非对源字符串的构成很了解,而且又不得不使用的情况,否则不要轻易使用这一模式。</p>
<p>典型应用(参考 <a href="http://topic.csdn.net/u/20080621/16/be94d56c-3f43-44fe-b542-3f29bd6714fb.html" rel="external nofollow" target="_blank">求一个好的算法</a>):</p>
<p>一个由字母组成的字符串,最长14位,要求每隔2位加一个逗号,最左边不加,求一个好的算法</p>
<p>例:“abcdefg” 返回“a,bc,de,fg”</p>
<p>代码实现:</p>
<div class="jb51code"><pre class="brush:csharp;">string test = "abcdefg";
string result = Regex.Replace(yourStr, @"(?<!^){2}", ",$0", RegexOptions.RightToLeft);</pre></div>
<p class="maodian"><a name="_lab2_1_9"></a></p><h3>2.2.6 None</h3>
<p>这一模式指定不开启任何模式。在.NET中RegexOptions 枚举值是按位“或”组合,None模式我目前只找到一种应用场景,就是在动态生成正则表达式时,动态指定模式时使用。</p>
<div class="jb51code"><pre class="brush:csharp;">/// <summary>
/// 动态生成正则参数列表
/// </summary>
/// <returns></returns>
private RegexOptions getParameter()
{
RegexOptions roList = RegexOptions.None;
if (cbIgnoreCase.Checked)
{
roList = roList | RegexOptions.IgnoreCase;
}
if (cbSingleline.Checked)
{
roList = roList | RegexOptions.Singleline;
}
if (cbMultiline.Checked)
{
roList = roList | RegexOptions.Multiline;
}
if (cbCompiled.Checked)
{
roList = roList | RegexOptions.Compiled;
}
if (cbRightToLelft.Checked)
{
roList = roList | RegexOptions.RightToLeft;
}
return roList;
}</pre></div>
<p class="maodian"><a name="_lab2_1_10"></a></p><h3>2.2.7 ExplicitCapture</h3>
<p>这一模式改变的是普通捕获组的匹配行为。将普通捕获组解释为非捕获组,只有显式命名的命名捕获组才当作捕获组使用。</p>
<p>捕获组的作用是将括号()内子表达式匹配到的内容保存到内存中一个组里,供以后引用,在.NET中捕获组有两种形式</p>
<p>(Expression) 普通捕获组</p>
<p>(?<name>Expression) 命名捕获组</p>
<p>其它形式的(?...)都不是捕获组。</p>
<p>但是(Expression)这种捕获组语法规则也带来一个副作用,在一些不得不使用()的场合,会默认为使用了捕获组,将匹配到的内容保存到内存中,而有些情况下这些内容并不需要关心的,浪费了系统资源,降低了匹配效率,所以才有了非捕获组(?:Expression)的出现,来抵消这一副作用。而非捕获组带来的另一个副作用的就是可读性的降低。</p>
<p>ExplicitCapture模式是为了在不牺牲匹配效率的前提下,提高正则表达式的可读性,一般在命名捕获组和普通捕获组混合出现,而又不关心普通捕获组的正则表达式中使用,如取链接和文字的正则表达式中</p>
<div class="jb51code"><pre class="brush:csharp;">MatchCollection mc = Regex.Matches(yourStr, @"(?is)<a((?!href=).)*href=(?<s>['""]?)(?<url>[^""'\s>]*)\k<s>[^>]*>(?<text>((?!</a>).)*)</a>", RegexOptions.ExplicitCapture);
foreach (Match m in mc)
{
richTextBox2.Text += m.Groups["url"].Value + "\n";
richTextBox2.Text += m.Groups["text"].Value + "\n";
}</pre></div>
<p>开启ExplicitCapture模式对正则表达式解释行为的影响,可以参考如下举例,匹配时间的正则表达式</p>
<p>未开启ExplicitCapture模式</p>
<div class="jb51code"><pre class="brush:csharp;">string test = "<li title=\"截至2009-07-28 20:45:49,用户的总技术分为:5988;截至2009-07-26日,用户的总技术分排名为:4133\">(...)</li>";
Regex reg = new Regex(@"(|2)(:){2}");
MatchCollection mc = reg.Matches(test);
foreach (Match m in mc)
{
richTextBox2.Text += m.Value + "\n";
richTextBox2.Text += m.Groups.Value + "\n";
richTextBox2.Text += m.Groups.Value + "\n";
}
/*--------输出--------
20:45:49
20
:49
*/</pre></div>
<p>开启ExplicitCapture模式</p>
<div class="jb51code"><pre class="brush:csharp;">string test = "<li title=\"截至2009-07-28 20:45:49,用户的总技术分为:5988;截至2009-07-26日,用户的总技术分排名为:4133\">(...)</li>";
Regex reg = new Regex(@"(|2)(:){2}");
MatchCollection mc = reg.Matches(test);
foreach (Match m in mc)
{
richTextBox2.Text += m.Value + "\n";
richTextBox2.Text += m.Groups.Value + "\n";
richTextBox2.Text += m.Groups.Value + "\n";
}
/*--------输出--------
20:45:49
*/</pre></div>
<p>一般来说,关心的只是整个正则表达式匹配的整体,启用了ExplicitCapture模式后,“(|2)”和“(:)”将不会被解释为捕获组,匹配到的内容也不会保存到内存中。</p>
<p>开启ExplicitCapture模式虽然可以提高正则表达式的可读性,但ExplicitCapture这一模式本身容易被人忽略,所以这种模式应用得也比较少。</p>
<p>一旦使用了ExplicitCapture模式,要注意的是,除非你清楚捕获组的编号规则,否则尽量不要再使用\number方式进行反向引用,可以使用\k<name>方式进行反向引用。</p>
<div class="jb51code"><pre class="brush:csharp;">Regex reg = new Regex(@"href=(['""]?)(?<url>[^'""\s>]+)\1", RegexOptions.ExplicitCapture);
MatchCollection mc = reg.Matches(yourStr);
foreach (Match m in mc)
{
richTextBox2.Text += m.Value + "\n";
}</pre></div>
<p>以上匹配链接的代码通常是得不到任何结果的,因为这里“\1”引用的内容,不再是“(['""]?)”匹配到的内容,而是“(?<url>[^'""\s>]+)”匹配到的内容,等价于“(?<url>[^'""\s>]+)\k<url>”,而这样的字符串,通常是不存在的。</p>
<p class="maodian"><a name="_lab2_1_11"></a></p><h3>2.2.8 IgnorePatternWhitespace</h3>
<p>这一模式忽略正则表达式中的非转义空白字符,并启用“#”后面的注释,通常用于增强正则表达式的可读性或是用于教学目的。</p>
<p>举个例子基本上就会明白了</p>
<div class="jb51code"><pre class="brush:csharp;">string test = "(One) and ( Two (Three) Four).";
Regex reg = new Regex(@"\( #普通开括弧
(?> #固化分组
\( (?<OPEN>) #遇到开括弧'OPEN'计数加1
| #分支结构
\) (?<-OPEN>) #遇到闭括弧'OPEN'计数减1
| #分支结构
[^()]+ #非括弧的其它任意字符
)* #以上子串出现0次或任意多次
(?(OPEN)(?!)) #判断是否还有'OPEN',有则说明不配对,什么都不匹配
\) #普通闭括弧
", RegexOptions.IgnorePatternWhitespace);
MatchCollection mc = reg.Matches(test);
foreach (Match m in mc)
{
richTextBox2.Text += m.Value + "\n";
}</pre></div>
<p>其中的空白字符被忽略,“#”及该行后面的内容被解释为注释。</p>
<p class="maodian"><a name="_lab2_1_12"></a></p><h3>2.2.9 ECMAScript</h3>
<p>这一模式将按ECMA的JavaScript语义来解析正则表达式。通常影响的是以下元字符的匹配行为</p>
<p>\w、\W、\d、\D、\s、\S</p>
<p>使得以上元字符只能匹配ASCII码字符,而不再是匹配相应的Unicode字符。</p>
<p>事实上比如需求中明确要求只匹配英文字母,数字,下划线,而不包含汉字等字符,那么完全可以用“”,而不使用“\w”,虽然不够简洁,但可读性更好,语义更明确。</p>
<p>这一模式在.NET中的应用比较少,如不清楚什么场景下应该使用,可以忽略这一模式。</p>
<p class="maodian"><a name="_lab2_1_13"></a></p><h3>2.2.10 CultureInvariant</h3>
<p>这一模式指定忽略语言中的区域性差异。一般与IgnoreCase模式一起使用,这一模式的应用场景很少,一般可以忽略,无需了解。</p>
<p class="maodian"><a name="_lab2_1_14"></a></p><h3>2.3 内联匹配模式</h3>
<p>内联匹配模式在正则表达式内部使用,可以改变局部子表达式的匹配行为,内联匹配模式支持的模式较少,仅有immsx五种,但使用起来更简洁,更灵活。在.NET中支持(?imnsx-imnsx:)和(?imnsx-imnsx)两种形式。</p>
<p>各内联匹配模式对应的全局匹配模式见匹配模式列表。以下仅就(?i)忽略大小写模式进行讲解,其它模式类同。</p>
<p class="maodian"><a name="_label3_1_14_2"></a></p><h4>2.3.1 (?imnsx-imnsx:)形式</h4>
<p>语法:(?i:Expression)</p>
<p>这种语法规则表达为括号内的子表达式开启忽略大小写模式。</p>
<p>举例:^(?i:{9,19})$</p>
<p>以上正则表示,首字符必须为大写字母,后面跟9到19个大小写字母。</p>
<div class="jb51code"><pre class="brush:csharp;">string[] test = new string[] { "Abc", "AbcdefGHIjklmn", "abcdefghijklmn" };
Regex reg = new Regex(@"^(?i:{9,19})$");
foreach (string s in test)
{
richTextBox2.Text += "源字符串: " + s.PadRight(15, ' ') + " 匹配结果: " + reg.IsMatch(s) + "\n";
}
/*--------输出--------
源字符串: Abc 匹配结果: False
源字符串: AbcdefGHIjklmn 匹配结果: True
源字符串: abcdefghijklmn 匹配结果: False
*/</pre></div>
<p>语法:(?-i:Expression)</p>
<p>这种语法规则表达为括号内的子表达式关闭忽略大小写模式。通常与全局匹配模式配合使用,表示全局为忽略大小写的,局部为严格区分大小写。</p>
<p>举例:<div id="(?-i:TEST)" [^>]*>[\w\s]+</div></p>
<p>以上正则表示,div标签中,仅匹配id为全大写的“TEST”的标签内容,其余标签不匹配。</p>
<div class="jb51code"><pre class="brush:csharp;">string test = "<DIV id=\"Test\" class=\"create\">first</div> and <DIV id=\"TEST\" class=\"delete\">second</div>";
Regex reg = new Regex(@"<div id=""(?-i:TEST)""[^>]*>[\w\s]+</div>", RegexOptions.IgnoreCase);
MatchCollection mc = reg.Matches(test);
foreach(Match m in mc)
{
richTextBox2.Text += m.Value + "\n";
}
/*--------输出--------
<DIV id="TEST" class="delete">second</div>
*/</pre></div>
<p class="maodian"><a name="_label3_1_14_3"></a></p><h4>2.3.2 (?imnsx-imnsx)</h4>
<p>语法:(?i) 或 (?-i)</p>
<p>(?i)为所在位置右侧的子表达式开启忽略大小写模式,直到出现(?-i)或者到表达式结束为止;(?-i)为所在位置右侧的子表达式关闭忽略大小写模式,直到出现(?i)或者到表达式结束为止。</p>
<p>通常在正则表达式开始位置使用(?i)来代替RegexOptions.IgnoreCase,使代码更简洁。如提取链接和文字的正则表达式</p>
<div class="jb51code"><pre class="brush:csharp;">MatchCollection mc = Regex.Matches(yourStr, @"(?is)<a(?:(?!href=).)*href=(['""]?)(?<url>[^""'\s>]*)\1[^>]*>(?<text>(?:(?!</a>).)*)</a>");
foreach (Match m in mc)
{
richTextBox2.Text += m.Groups["url"].Value + "\n";
richTextBox2.Text += m.Groups["text"].Value + "\n";
}</pre></div>
<p class="maodian"><a name="_label2"></a></p><h2>3、小结</h2>
<p>.NET中的正则匹配模式基本上已讲解完了,最后再来总结一下各匹配模式的应用场景吧。</p>
<div><table><tbody><tr><td><p><strong>RegexOption </strong><strong>成员</strong></p></td><td><p><strong>内联字符</strong></p></td><td><p><strong>作用/应用场景</strong></p></td></tr><tr><td><p>None</p></td><td><p>N/A</p></td><td><p>指定不设置任何选项。</p>
<p>一般在动态指定匹配模式时使用。</p></td></tr><tr><td><p>IgnoreCase</p></td><td><p>i</p></td><td><p>指定不区分大小写的匹配。</p>
<p>只有在正则表达式中,注意是正则表达式中,而不是待匹配的源字符串中,涉及到大小写形式的元字符或是字符序列时,才使用IgnoreCase模式。</p></td></tr><tr><td><p>Multiline</p></td><td><p>m</p></td><td><p>指定多行模式。更改 <strong>^</strong> 和 <strong>$</strong> 的含义,以使它们分别与任何行的开头和结尾匹配,而不只是与整个字符串的开头和结尾匹配。</p>
<p>只有在正则表达式中涉及到多行的“^”和“$”的匹配时,才使用Multiline模式。</p></td></tr><tr><td><p>ExplicitCapture</p></td><td><p>n</p></td><td><p>指定唯一有效的捕获是显式命名或编号的 (?<name>…) 形式的组。这允许圆括号充当非捕获组,从而避免了由 (?:…) 导致的语法上的笨拙。</p>
<p>ExplicitCapture模式是为了在不牺牲匹配效率的前提下,提高正则表达式的可读性,一般在命名捕获组和普通捕获组混合出现,而又不关心普通捕获组的正则表达式中使用。</p></td></tr><tr><td><p>Compiled</p></td><td><p>N/A</p></td><td><p>指定正则表达式将被编译为程序集。生成该正则表达式的 Microsoft 中间语言 (MSIL) 代码;以较长的启动时间为代价,得到更快的执行速度。</p>
<p>以下场景不适合使用:</p>
<p>1. 对匹配效率没有要求的场景;</p>
<p>2. 非常简单的正则表达式;</p>
<p>3. 极少调用的方法中声明的正则表达式;</p>
<p>4. 循环体中声明的正则表达式(除了动态生成的正则表达式,否则不要在循环体内声明正则表达式);</p>
<p>5. 静态方法中声明的正则表达式(静态方法每次调用都需要重新编辑正则表达式,使用Compiled模式只会降低效率)。</p></td></tr><tr><td><p>Singleline</p></td><td><p>s</p></td><td><p>指定单行模式。更改句点字符 (.) 的含义,以使它与每个字符(而不是除 <strong>\n</strong> 之外的所有字符)匹配。</p>
<p>单行模式通常在匹配有换行的文本时使用,采用小数点+单行模式的方式匹配任意字符,在.NET中是效率最高的。</p></td></tr><tr><td><p>IgnorePatternWhitespace</p></td><td><p>x</p></td><td><p>指定从模式中排除非转义空白并启用数字符号 (#) 后面的注释。请注意,空白永远不会从字符类中消除。</p>
<p>为了提高可读性,或是为正则表达式加注释时使用。</p></td></tr><tr><td><p>RightToLeft</p></td><td><p>N/A</p></td><td><p>指定搜索是从右向左而不是从左向右进行的。具有此选项的正则表达式将移动到起始位置的左边而不是右边。</p>
<p>除非对源字符串的构成很了解,而且又不得不使用的情况,否则不要轻易使用这一模式。</p></td></tr><tr><td><p>ECMAScript</p></td><td><p>N/A</p></td><td><p>指定已为表达式启用了符合 ECMAScript 的行为。此选项仅可与 <strong>IgnoreCase</strong> 和 <strong>Multiline</strong> 标志一起使用。将 <strong>ECMAScript</strong> 同任何其他标志一起使用将导致异常。</p>
<p>除非对这一模式应用场景很了解,否则不要使用。</p></td></tr><tr><td><p>CultureInvariant</p></td><td><p>N/A</p></td><td><p>指定忽略语言中的区域性差异。</p>
<p>很少使用,一般无需了解。</p></td></tr></tbody></table></div>
頁:
[1]