.NET正则基础——.NET正则类及格局运用

1        概述

初学正则时,对于Regex类不熟识,遭受题目不知情该用哪个种类形式解决,本文结合一些正则应用的超人应用场景,介绍一下Regex类的基本接纳。那里关键进行.NET类的介绍,对于正则的行使,不做长远研讨。

正则的选取最后都是展开情势的合营,而按照目的的不一致,基本上可以分为以下三种拔取:验证、提取、替换、分割。结合.NET提供的控件、类以及类的法门,可以很便利的达成这个使用。

以下将构成一些超人的运用场景,对.NET中普遍的类、方法及品质进行介绍。本文目的在于.NET类基础用法的指点,对于里边涉嫌到的正则表明式不做深刻琢磨。本文适合于在.NET平台下利用正则的初学者。

2        基础运用

2.1     验证

表达的目的是为着判定输入的源字符串是不是符合某一法则或规则,根据需要的两样,可能是校验整个源字符串,也可能只是校验其中一个子串。

讲明在.NET中一般有二种采用,一种是在验证控件RegularExpressionValidator中,另一种是在程序中。

2.1.1  验证控件RegularExpressionValidator

RegularExpressionValidator是.NET自带的一种客户端验证控件,通过不难的设置,即可到位对某控件输入值的校验。

主导使用语法如下:

       
<asp:RegularExpressionValidator

           
ID=”RegularExpressionValidator1″

           
runat=”server”

           
ControlToValidate=”TextBox1″

           
ErrorMessage=”RegularExpressionValidator”

           
ValidationExpression=”^([1-9][0-9]*|0)(\.[0-9][2])?$”>

        </asp:RegularExpressionValidator>

对此RegularExpressionValidator控件不做过多介绍,只是说Bellamy下用到时须要留意的几点:

1、 
RegularExpressionValidator进行的是客户端验证;

2、 
RegularExpressionValidator中正则运用的是JavaScript语法规则;

3、 
RegularExpressionValidator控件无法证实输入是或不是为空。

由于RegularExpressionValidator做的是客户端验证,很不难被跳过,所以在应用RegularExpressionValidator验证的同时,还要做服务器端验证。

RegularExpressionValidator最后是要生成客户端的JavaScript代码举办求证的,所以RegularExpressionValidator使用的正则须要符合JavaScript语法规则,与.NET的几点分别:

1、 
不帮衬逆序环视,也就是(?<=Expression)和(?<!Expression)那样的语法;

2、 
元字符仅接济ASCII码,即\w等价于[a-zA-Z0-9_],\d等价于[0-9]

RegularExpressionValidator控件一般是用来表达某一控件输入的字符串全部是不是适合某一平整的,所以平常状态下“^”和“$”是必需的;在使用“|”表示“或”的关系时,一定要用“()”来限定“|”效用范围,比如0-100得以编写“^([1-9]?[0-9]|100)$”。

RegularExpressionValidator是不可能表达输入是或不是为空的,验证是或不是为空要用RequiredFieldValidator控件。

RegularExpressionValidator验证控件是.NET为便利客户端验证封装的一组求证控件之一,但鉴于RegularExpressionValidator受限于匡助的正则语法规则,只可以做简单的格式校验,一些复杂的校验可以通过友好写JavaScript代码来兑现,也是很简短的。

2.1.2  程序验证——IsMatch()

次第中的校验基本上就是应用IsMatch方法,验证的靶子可能是源字符串的完好,也可能只是中间一个子串。

表明源字符串的完全是不是适合某一规则,与应用RegularExpressionValidator时的须要大多一致,不过由于是在.NET程序中,所以利用的是.NET的语法,比JavaScript中要强大得多。比如验证一个文本框输入的字符串是不是顺应某一条条框框,就是一个头名的表达整体的须求。

举例1:验证textBox1输入内容,须要整数有的为0或正整数,小数可有可无,有小数时务必为2位。

           
Regex reg = new Regex(@”^(?:[1-9][0-9]*|0)(?:\.[0-9]{2})?$”);

           
if
(reg.IsMatch(textBox1.Text))

           
{

                richTextBox2.Text =
“输入格式正确!”;

           
}

           
else

           
{

                richTextBox2.Text =
“输入格式错误!”;

           
}

由于是对源字符串的全部进行表明,所以“^”和“$”是不可或缺的。否则验证的结果可能是一无可取的,比如正则表达式“(?:[1-9][0-9]*|0)(?:\.[0-9]{2})?”,在输入“0.123”时是可以包容成功的,匹配结果为“0.12”,此时正则只起到了万分的功能,没有起到表达的成效。

证实源字符串的片段是还是不是相符某一条条框框,就是对于源字符串中子串的校验,日常是用来判定源字符串中是还是不是带有,或是不包含符合某一原理的子串,效率类似于string类中的IndexOf。

举例2(参考问五个正则表达式):

数据:

1985aaa1985bb

bcae1958fiefadf1955fef

atijc1944cvkd

df2564isdjfef2564d

abc1234def5678ghi5678jkl

须求1:验证字符串中任意地点出现的连接三个数字在整个字符串中是或不是有再一次,有再一次为True,无重复为False。

上述数量必要1的表明结果为True的应为:

1985aaa1985bb

df2564isdjfef2564d

abc1234def5678ghi5678jkl

因为须求中指明是即兴地点的连日4个数字是或不是有再次,所以在找到重复前,要遍历源字符串中每一个职分时行验证,那样就无法限定伊始标识符“^”;而在合营进程中,除非一贯到最后仍找不到再次,否则一旦匹配到有重复的职务就足以了,那样也不须要收尾标识符“$”,所以那是出色的对字符串的子串行验证的必要。

代码达成:

string[]
test = new string[] { “1985aaa1985bb”, “bcae1958fiefadf1955fef”, “atijc1944cvkd”, “df2564isdjfef2564d”, “abc1234def5678ghi5678jkl”
};

Regex
reg = new Regex(@”(\d{4})(?:(?!\1).)*\1″);

foreach
(string s in test)

{

      richTextBox2.Text += “源字符串: 

  • s.PadRight(25, ‘ ‘) + “验证结果: 
  • reg.IsMatch(s) + “\n”;

}

/*——–输出——–

源字符串:  1985aaa1985bb            验证结果: 
True

源字符串:  bcae1958fiefadf1955fef   验证结果: 
False

源字符串:  atijc1944cvkd            验证结果: 
False

源字符串:  df2564isdjfef2564d       验证结果: 
True

源字符串:  abc1234def5678ghi5678jkl
验证结果: 
True

*/

鉴于涉及到了再次难点,所以那里运用了反向引用,对于反向引用的细节,可以参照正则基础之——反向引用

需要2:验证字符串中率先个冒出的连日4个数字是不是有双重,有双重为True,无重复为False。

上述数据要求2的证实结果为True的应为:

1985aaa1985bb

df2564isdjfef2564d

因为需求中指明是第四个是还是不是有再度,所以必要有起初标识符“^”,来保管是第四个冒出的接二连三4个数字;而在非凡进度中,除非平素到最终仍找不到再也,否则一经匹配到有双重的职位就能够了,那样也不需求收尾标识符“$”,所以这仍是对字符串的子串行验证的需要,只可是相对于要求1的话,加了一个限量条件。

代码达成:

string[]
test = new string[] { “1985aaa1985bb”, “bcae1958fiefadf1955fef”, “atijc1944cvkd”, “df2564isdjfef2564d”, “abc1234def5678ghi5678jkl”
};

Regex
reg = new Regex(@”^(?:(?!\d{4}).)*(\d{4})(?:(?!\1).)*\1″);

foreach
(string s in test)

{

     richTextBox2.Text += “源字符串: 

  • s.PadRight(25, ‘ ‘) + “验证结果: 
  • reg.IsMatch(s) + “\n”;

}

/*——–输出——–

源字符串:  1985aaa1985bb            验证结果: 
True

源字符串:  bcae1958fiefadf1955fef   验证结果: 
False

源字符串:  atijc1944cvkd            验证结果: 
False

源字符串:  df2564isdjfef2564d       验证结果: 
True

源字符串:  abc1234def5678ghi5678jkl
验证结果: 
False

*/

2.2     提取——Match()、Matches()

领到重若是从源字符串中,取得一个或七个符合某一规律或规则的子串。一般的话,在字符串处理中,提取使用比较常见。提取时用得相比较多的是Match()和Matches()方法,以及结果处理时Match类和MatchCollection类的一些措施,有时也会用到Capture类的部分格局。

2.2.1  提取单次匹配内容——Match()

当要求领取的情节唯有一个,或是只供给得到第两回中标匹配的内容时,能够动用Match()方法。当使用Match()方法时,只要在某一职位匹配成功,就不再接续尝试匹配,并重回一个Match类型的目的。

举例:提取姓名

源字符串:姓名:张三,性别:男,年龄:**25**

代码达成:

string
test = “姓名:张三,性别:男,年龄:25”;

Regex
reg = new Regex(@”(?<=姓名:)[^,]+”);

Match
m = reg.Match(test);

if
(m.Success)    //验证是还是不是合营成功

{

     
richTextBox2.Text = m.Value;

}

/*——–输出——–

张三

*/

即使Match()只是取四遍匹配,不过足以经过捕获组来博取多个指定子串,比如获取第四个<a…>标签的链接和文书。

string
test = “abc<a
href=\”www.test1.com\”>测试一</a>def<a
href=\”www.test2.com\”>测试二</a>ghi”;

Regex
reg = new Regex(@”(?is)<a(?:(?!href=).)href=([‘””]?)(?<url>[^””\s>]*)\1[^>]*>(?<text>(?:(?!</?a\b).)*)</a>”);

Match
m = reg.Match(test);

if(m.Success)

{

     richTextBox2.Text +=
m.Groups[“url”].Value + “\n”;      //链接

     richTextBox2.Text +=
m.Groups[“text”].Value + “\n”;    
//文本

}

/*——–输出——–

www.test1.com

测试一

*/

对此捕获组捕获结果的引用,还有一种艺术

string
test = “abc<a
href=\”www.test1.com\”>测试一</a>def<a
href=\”www.test2.com\”>测试二</a>ghi”;

Regex
reg = new Regex(@”(?is)<a(?:(?!href=).)href=([‘””]?)(?<url>[^””\s>]*)\1[^>]*>(?<text>(?:(?!</?a\b).)*)</a>”); Match m = reg.Match(test);

if(m.Success)

{

     richTextBox2.Text +=
m.Result(“${url}”) + “\n”;      //链接

     richTextBox2.Text +=
m.Result(“${text}”) + “\n”;    
//文本

}

/*——–输出——–

www.test1.com

测试一

*/

那二种办法获得的结果都是同等的,使用哪一类,平常根据个体习惯而定。

2.2.2  提取数十次格外内容——Matches()

当需求领取的始末有两个,并且需求领取所有符合规律的子串时,可以采用Matches()方法。当使用Matches()方法时,须求遍历源字符串的每一个地点展开尝试匹配,匹配停止重回一个MatchCollection类型的目的。

对于1.2.1节事关的领到链接和文书的例证,假如提取的是总体链接和文件,而不仅是率先个时,可以应用Matches()方法。

string
test = “abc<a
href=\”www.test1.com\”>测试一</a>def<a
href=\”www.test2.com\”>测试二</a>ghi”;

Regex
reg = new Regex(@”(?is)<a(?:(?!href=).)href=([‘””]?)(?<url>[^””\s>]*)\1[^>]*>(?<text>(?:(?!</?a\b).)*)</a>”);

MatchCollection
mc = reg.Matches(test);

foreach(Match m in mc)

{

     richTextBox2.Text +=
m.Groups[“url”].Value + “\n”;      //链接

     richTextBox2.Text +=
m.Groups[“text”].Value + “\n”;    
//文本

}

/*——–输出——–

www.test1.com

测试一

www.test2.com

测试二

*/

对此Matches(),某些场景下,也可以透过Count属性,用做统计符合某一法则的子串现身的次数,例如计算字符串中单独的“3”出现的次数。

string
test = “137,1,33,4,3,6,21,3,35,93,2,98”;

Regex
reg = new Regex(@”\b3\b”);

int
count = reg.Matches(test).Count;    //2

此时关注的只是分外成功的次数,对于匹配的内容是不关怀的,所以达成那种要求时,正则应尽可能精简,能完成目的即可,那样可以加速匹配效能,裁减资源占用。比如下边的提取链接的源字符串中,计算<a…>标签出现的次数,一般的话,如下代码即可达到目标了。

string
test = “abc<a
href=\”www.test1.com\”>测试一</a>def<a
href=\”www.test2.com\”>测试二</a>ghi”;

Regex
reg = new Regex(@”(?i)<a\b”);

int
count = reg.Matches(test).Count;   
//2

2.2.3  捕获组匹配进度集合——Capture

在一些情状下,一个正则表明式全部同盟五次时,其中的捕获组可能会协作很多次。

举例

源字符串:<region
name=oldslist col=1 row=2 order=asc>abcsadf </region>
jfdsajf  <region name=newslist
class=list col=4 row=10 order=desc>abcsadf
</region>

要求:提取出每个region的质量和属性值,按region分组。

对此这几个须要,可以先提取出所有region,再对每个region标签提取它的性质和属性值,但如此做比较麻烦,可以考虑在一个正则表明式中提取。由于特性的个数是不定点的,所以不能用固定个数的量词来匹配属性对,正则可以写为

(?is)<region\s+(?:(?<key>[^\s=]+)=(?<value>[^\s>]+)\s*)+>

此时假如还用Groups来取匹配结果时,由于Groups只保留最终五次匹配结果,所以只好取到最后一遍匹配成功的子串。这时就要用到Captures属性。

string
test = “<region name=oldslist col=1
row=2 order=asc>abcsadf </region> jfdsajf  <region name=newslist class=list
col=4 row=10 order=desc>abcsadf
</region>”;

MatchCollection
mc = Regex.Matches(test, @”(?is)<region\s+(?:(?<key>[^\s=]+)=(?<value>[^\s>]+)\s*)+>”);

for
(int i = 0; i < mc.Count;
i++)

{

    richTextBox2.Text += “第”

  • (i + 1) + “个region的属性:\n”;

   
for (int j = 0; j < mc[i].Groups[“key”].Captures.Count; j++)

   
{

        richTextBox2.Text += “属性: “

  • mc[i].Groups[“key”].Captures[j].Value.PadRight(10,
    ‘ ‘) + ” 
    值: “
  • mc[i].Groups[“value”].Captures[j].Value + “\n”;

   
}

   
richTextBox2.Text += “\n”;

}

/*——–输出——–

第1个region的属性:

属性: name        值:
oldslist

属性: col         值:
1

属性: row         值:
2

属性: order       值:
asc

 

第2个region的属性:

属性: name        值:
newslist

属性: class       值:
list

属性: col         值:
4

属性: row         值:
10

属性: order       值:
desc

*/

Group实际上是Capture的一个见面,在捕获组只卓殊一个子串时,这么些集合唯有一个元素,而在捕获组先后匹配多个子串时,Groups[i].Value只保留最终一个金童玉女结果,而Capture集合却得以记录匹配进程中出色到的富有子串。

Capture的运用场景并不多,对于地方的事例,假设不行使
Capture,可以透过分次匹配的办法贯彻,不过在一些复杂的表明式中,很难展开分次匹配,那时Capture就相比较有用了。

2.3      替换

轮换紧要是从源字符串中,将符合某一法则或规则的子串替换为任何内容。一般的话,在字符串处理中,替换应用也正如宽泛。替换重若是用Replace()方法,在一部分交替规则复杂的利用场景中,也说不定会用到委托方法。

2.3.1  一般替换

轮换的目标很明朗,只要找出待替换子串的原理,替换为对象子串就可以了。

举例1

源字符串:<img
src=”/imgs/logo.jpg”> abc <img
src=”http://www.test.com/imgs/top.gif"&gt;

须求:将绝对地址替换为绝对地址,已经是纯属地址的不替换。

string
test = “<img
src=\”/imgs/logo.jpg\”> abc <img
src=\”http://www.test.com/imgs/top.gif\\"&gt;”;

Regex
reg = new Regex(@”(?i)(?<=src=([‘””]?))(?!http://)(?=\[^'""\\s&gt;\]+\\1)”);

string
result = reg.Replace(test, “http://www.test.com“);

/*——–输出——–

<img
src=”http://www.test.com/imgs/logo.jpg"&gt; abc <img
src=”http://www.test.com/imgs/top.gif"&gt;

*/

此间需求表达的是,在.NET中只提供了一个Replace()方法,没有提供类似于Java中的replaceAll()和replaceFirst()两种艺术,所以一旦在.NET中只替换第二回面世的符合规律的子串时,须求在正则表达式中拍卖。

举例2

源字符串:abc123def123ghi

急需:将首先次出现的“123”替换为空,其余地方不替换。

string
test = “abc123def123ghi”;

Regex
reg = new Regex(@”(?s)^((?:(?!123).)*)123″);

string
result = reg.Replace(test, “$1”);

/*——–输出——–

abcdef123ghi

*/

这时用“^”限定只替换首回面世的子串,由于半数以上正则引擎都对“^”举行了优化,所以正则表明式在地方0处匹配成功或破产后,将不再对其他地点举办尝试匹配。

2.3.2  替换中的委托方法运用

对于有些比较复杂的轮换规则,可能会用到委托方法,由于那种使用属于比较出色的一种接纳,所以将在前面的篇章中独立开展介绍。

2.4     分割

划分就是用适合某一原理的子串,将源字符串分割成一个数组,首要运用的是Split()方法。由于Regex的Split()方法中,并不曾提供类似于string的Split()方法的StringSplitOptions.RemoveEmptyEntries参数,而假若符合规律的子串出现在起来或最终时,会现出不需求的空域,那时须要在正则表达式中展开一下处理。

举例1

源字符串:汉字123文字english

须求:按英文单词和非英文单词进行私分(英文单词包罗由a-z,A-Z,0-9,_组成的子串)。

string
str = “汉字123文字english”;

string[]
result = Regex.Split(str, @”(?<!^)\b(?!$)”, RegexOptions.ECMAScript);

foreach
(string s in result)

{

    
richTextBox2.Text += s + “\n”;

}

/*——–输出——–

汉字

123

文字

English

*/

此处分别用“(?<!^)”和“(?!$)”来限制不以初始或最终的子串举行分割,结果中也就不会现出不必要的空白了。

还有部分使用,其实可以算作是正则就用技术范畴的了。

举例2

源字符串:Left(姓名,1),Left(姓名,1) ,  Left    (姓名,1)

需求:以不在“()”内的“,”举行划分。

string
test = “Left(姓名,1),Left(姓名,1) ,  Left    (姓名,1)”;

Regex
reg = new Regex(@”(?<!\([^)]*),(?![^(]*\))”);

string[]
sArray = reg.Split(test);

foreach
(string s in sArray)

{

    
richTextBox2.Text += s + “\n”;

}

/*——–输出——–

Left(姓名,1)

Left(姓名,1)

  Left    (姓名,1)

*/

拔取正则的Split()方法时,有某些要求注意,那就是假若正则中冒出了捕获组,那么捕获组匹配到的情节也会保留到分割结果中。

以下举例不做详细表达,看下结果大多就精晓了。

string
test = “aa11<bbb>cc22<ddd>ee”;

string[]
temp = Regex.Split(test, @”[0-9]+(<[^>]*>)”);

foreach
(string s in temp)

{

    
richTextBox2.Text += s + “\n”;

}

/*——–输出——–

aa

<bbb>

cc

<ddd>

ee

*/

若果不想把捕获组匹配到的始末也存入结果,可以动用非捕获组。

string
test = “aa11<bbb>cc22<ddd>ee”;

string[]
temp = Regex.Split(test, @”[0-9]+(?:<[^>]*>)”);

foreach
(string s in temp)

{

    
richTextBox2.Text += s + “\n”;

}

/*——–输出——–

aa

cc

ee

*/

3       增加应用

此地介绍部分或者波及到的局部.NET中的正则增加应用。

3.1     动态变化正则时的转义——Escape()

突发性需求基于部分变量动态变化正则表明式,那时假若变量中蕴藏正则中的元字符,会被解析成元字符,就可能会导致正则编译不通过,从而导致程序分外,须要对变量举行转义处理。Regex.
Escape()方法通过轮换为转义码来转义最小的字符集(\、*、+、?、|、{、[、(、)、^、$、.、# 和空白)。

譬如依照用户输入的id取相应的div标签,id中从不元字符时,可以赢得正确结果。

string
test = “<div
id=\”test1\”>abc</div><div
id=\”test2\”>def</div>”;           

string[]
ids = new string[] { “test1”, “test2” };

foreach
(string id in ids)

{

    
Regex reg = new Regex(@”(?is)<div\s+id=””” + id + @”””[^>]*>(?:(?!</?div\b).)*</div>”);

    
MatchCollection mc =
reg.Matches(test);

    
foreach (Match m in mc)

    
{

         
richTextBox2.Text += m.Value + “\n”;

    
}

}

/*——–输出——–

<div
id=”test1″>abc</div>

<div
id=”test2″>def</div>

*/

不过假若输入的id中一经现身未经转义的元字符,如“abc(”,就会抛类似于上面的老大。

正在分析“(?is)<div\s+id=”abc(“[^>]*>(?:(?!</?div\b).)*</div>”- ) 不足。

此时可以用Escape()方法对输入的变量举办转义处理。

string
test = “<div
id=\”test1\”>abc</div><div
id=\”test2\”>def</div>”;           

string[]
ids = new string[] { “test1”, “test2”, “abc(” };

foreach
(string id in ids)

{

    
Regex reg = new Regex(@”(?is)<div\s+id=””” + Regex.Escape(id) + @”””[^>]*>(?:(?!</?div\b).)*</div>”);

    
MatchCollection mc =
reg.Matches(test);

    
foreach (Match m in mc)

    
{

        
 richTextBox2.Text +=
m.Value + “\n”;

    
}

}

/*——–输出——–

<div
id=”test1″>abc</div>

<div
id=”test2″>def</div>

*/

利用Escape()方法转义后,就可以得到不错的结果,而不会抛非常了。

3.2     静态方法

.NET中部分Regex类的宽泛方法都提供了对应的静态方法,能够不显式的注解Regex对象,而直白调用相应的章程,书写起来更便利,代码更简单、易读。

例如替换IP地址最终一节为“*”,只需一行代码。

string
result = Regex.Replace(“10.27.123.12″, @”\d+$”, “*”);  
//10.27.123.*

静态方法每一回调用都会成立一个暂时的Regex对象,使用将来自由,所以每便调用静态方法时,都会重复编译,而那将会下落执行成效。由此在循环或是频仍调用的格局中,不相符采用静态方法,而需求举行显式表明Regex对象。

只是对于有些只调用四回,或是对履行作用没有要求的气象下,静态方法则是很正确的抉择。