C# 正则表达式及常用正则表达式


元字符


描述


.点


匹配任何单个字符。例如正则表达式r.t匹配这些字符串:rat、rut、r t,但是不匹配root。


$


匹配行结束符。例如正则表达式weasel$ 能够匹配字符串"He's a weasel"的末尾 ,但是不能匹配字符串"They are a bunch of weasels."


^


匹配一行的开始。例如正则表达式^When in能够匹配字符串"When in the course of human events"的开始,但是不能匹配"What and When in the"


*


匹配0或多个正好在它之前的那个字符。例如正则表达式.*意味着能够匹配任意数量的任何字符。


\


这是引用符,用来将这里列出的这些元字符当作普通的字符来进行匹配。例如正则表达式\$被用来匹配美元符号,而不是行尾,类似的,正则表达式\.用来匹配点字符,而不是任何字符的通配符。


[ ]


[c1-c2]


[^c1-c2]


匹配括号中的任何一个字符。例如正则表达式r[aou]t匹配rat、rot和rut,但是不匹配ret。可以在括号中使用连字符-来指定字符的区间,例如正则表达式[0-9]可以匹配任何数字字符;还可以制定多个区间,例如正则表达式[A-Za-z]可以匹配任何大小写字母。另一个重要的用法是“排除”,要想匹配除了指定区间之外的字符——也就是所谓的补集——在左边的括号和第一个字符之间使用^字符,例如正则表达式[^269A-Z] 将匹配除了2、6、9和所有大写字母之外的任何字符。


\< \>


匹配词(word)的开始(\<)和结束(\>)。例如正则表达式\<the\>能够匹配字符串"for the wise"中的"the",但是不能匹配字符串"otherwise"中的"the"。注意:这个元字符不是所有的软件都支持的。


\( \)


 \( 和 \) 之间的表达式定义为“组”(group),并且将匹配这个表达式的字符保存到一个临时区域(一个正则表达式中最多可以保存9个),它们可以用 \1 到\9 的符号来引用。


|


将两个匹配条件进行逻辑“或”(Or)运算。例如正则表达式(him|her) 匹配"it belongs to him"和"it belongs to her",但是不能匹配"it belongs to them."。注意:这个元字符不是所有的软件都支持的。


+


匹配1或多个正好在它之前的那个字符。例如正则表达式9+匹配9、99、999等。注意:这个元字符不是所有的软件都支持的。


?


匹配0或1个正好在它之前的那个字符。注意:这个元字符不是所有的软件都支持的。


\{i\}


\{i,j\}


匹配指定数目的字符,这些字符是在它之前的表达式定义的。例如正则表达式A[0-9]\{3\} 能够匹配字符"A"后面跟着正好3个数字字符的串,例如A123、A348等,但是不匹配A1234。而正则表达式[0-9]\{4,6\} 匹配连续的任意4个、5个或者6个数字字符。注意:这个元字符不是所有的软件都支持的。


 


/*
******************匹配常见的格式*********************/ //var pattern=/^\w{5,10}$/; //最少5位的下划线、字母、数字,\w+相当于\w{1,} \w*相当于\w{0,} \w?相当于\w{0,1} \w{5,10}5到10位 //var pattern1=/^[1-9]\d{7}((0\d)|(1[0-2]))(([0|1|2]\d)|3[0-1])\d{3}$/;//15位的身份证 //var pattern2=/^[1-9]\d{5}[1-9]\d{3}((0\d)|(1[0-2]))(([0|1|2]\d)|3[0-1])\d{4}$/;//18位的身份证 //var pattern=/^\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*$/;//匹配E-MALI地址 //var pattern=/^http:\/\/([\w-]+\.)+[\w-]+(\/[\w- .\/?%&=]*)?/;//匹配网址 //var pattern=/http(s)?:\/\/([\w-]+\.)+[\w-]+(\/[\w- .\/?%&=]*)?/;//匹配网址 //var pattern=/^[\u4e00-\u9fa5]$/;//匹配中文字符(单个汉字) //var pattern=/^[1-9]\d{5}(?!\d)$/;//匹配邮政编码 //var pattern=/^[1-2][0-9][0-9][0-9]-[0-1]{0,1}[0-9]-[0-3]{0,1}[0-9]$/;//匹配日期 如:1900-01-01 //var pattern=/^[^\x00-\xff]$/;//匹配双字节字符(包括汉字在内的单个字符) //var pattern=/^<(.*)>.*<\/\1>|<(.*) \/>$/;//匹配HTML标记 //var pattern=/<(\S*?)[^>]*>.*?<\/\1>|<.*? \/>/;//匹配HTML标记 //var pattern=/^\n[\s| ]*\r$/;//可以用来删除空白行 //var pattern=/^(\s*)|(\s*)$///可以用来删除行首尾的空白字符(包括空格、制表符、换页符等等) //var pattern=/^[a-zA-Z][a-zA-Z0-9_]{4,15}$/;//字母开头,限制5-16字节,允许字母数字下划线 //var pattern=/^\d{3}-\d{8}|\d{4}-\d{7,8}$/;//匹配国内电话 如:0739-8888888(8) 或 020-88888888 //var pattern=/^[1-9][0-9]{4,}$/;//匹配QQ号码 腾讯QQ号从10000开始 //var pattern=/^\d+\.\d+\.\d+\.\d+$/;//匹配IP地址 /*******************匹配特定数字*********************/ //var pattern=/^(\w)\1{4,}*$/;//匹配整数 //var pattern=/-?[1-9]\d*$/;//匹配整数 //var pattern=/^[1-9]\d*$/;//匹配正整数 //var pattern=/^-[1-9]\d*$/;//匹配负整数 //var pattern=/^[1-9]\d*|0$/;//匹配非负整数 //var pattern=/^-[1-9]\d*|0$/;//匹配非正整数 //var pattern=/^[1-9]\d*\.\d*|0\.\d*[1-9]\d*$/;//匹配正浮点数 //var pattern=/^-([1-9]\d*\.\d*|0\.\d*[1-9]\d*)$/;//匹配负浮点数 //var pattern=/^-?([1-9]\d*\.\d*|0\.\d*[1-9]\d*|0?\.0+|0)$/;//匹配浮点数 //var pattern=/^[1-9]\d*\.\d*|0\.\d*[1-9]\d*|0?\.0+|0$/;//匹配非负浮点数 //var pattern=/^(-([1-9]\d*\.\d*|0\.\d*[1-9]\d*))|0?\.0+|0$/;//匹配非正浮点数 /********************匹配特定字符串*****************/ //var pattern=/^[A-Za-z]+$/;//匹配由26个英文字母组成的字符串 //var pattern=/^[A-Z]+$/;//匹配由26个英文字母的大写组成的字符串 //var pattern=/^[a-z]+$/;//匹配由26个英文字母的小写组成的字符串 //var pattern=/^[A-Za-z0-9]+$/;//匹配由数字和26个英文字母组成的字符串 //var pattern=/^\w+$/;//匹配由数字、26个英文字母或者下划线组成的字符串 <script> if(!pattern.test(document.getElementById("content").value)){ alert("请输入正确的格式!"); return false; } } </script> <% ‘限制只能输入中文:onkeyup="value=value.replace(/[^\u4E00-\u9FA5]/g,‘‘)" onbeforepaste="clipboardData.setData(‘text‘,clipboardData.getData(‘text‘).replace(/[^\u4E00-\u9FA5]/g,‘‘))" ‘限制只能输入全角字符:onkeyup="value=value.replace(/[^\uFF00-\uFFFF]/g,‘‘)" onbeforepaste="clipboardData.setData(‘text‘,clipboardData.getData(‘text‘).replace(/[^\uFF00-\uFFFF]/g,‘‘))" ‘限制只能输入数字:onkeyup="value=value.replace(/[^\d]/g,‘‘) "onbeforepaste="clipboardData.setData(‘text‘,clipboardData.getData(‘text‘).replace(/[^\d]/g,‘‘))" ‘限制只能输入数字和英文:onkeyup="value=value.replace(/[\W]/g,‘‘) "onbeforepaste="clipboardData.setData(‘text‘,clipboardData.getData(‘text‘).replace(/[^\d]/g,‘‘))" %>
匹配模式+环视(顺序环视、逆序环视)+贪婪与非贪婪

 

RegexOptions.Multiline

“^”匹配结果分析

在不开启多行模式时,“^”只匹配字符串的开始位置,也就是位置0。

在开启了多行模式后,“^”匹配字符串开始位置和每个“\n”之后的行起始位置。

 

“$”匹配结果分析

在不开启多行模式时,如果字符结尾是“\n”,那么“$”会匹配结尾“\n”之前和结束两个位置。

在开启多行模式后,“$”匹配每行“\n”之前的位置和字符串结束位置。

 

需要注意的是,在.NET中,无论是否开启多行模式,“^”和“$”匹配的都只是一个位置,是零宽度的。其它语言中“^”和“$”的意义可能会有所不同。

只有在正则表达式中涉及到多行的“^”和“$”的匹配时,才使用Multiline模式。

 

RegexOptions.Compiled

Compiled改变的是.NET中正则表达式的编译方式。启用了Compiled模式,会延长启动时间,占用更多的内存,会提高匹配速度。当然,对最终性能的影响,需要根据具体问题综合考虑的。这一模式也是被“滥”用最多的模式之一。

 

程序运行过程中,第一次遇到正则表达式,需要加载正则引擎,对正则表达式进行必要的语法检查,并做适当的优化,最后把它转换为适合正则引擎应用的形式。这种“解析”过程,对于复杂的正则表达式,频繁调用或是匹配较大的数据源时,对效率的影响较大。

 

这时可以在构建正则表达式时开启Compiled模式。这样做会将正则表达式直接编译为MSIL代码,在正则匹配过程中,可以由JIT优化为更快的本地机器代码,获得更高的匹配速度。但这种方式会降低正则的解析速度,占用更多的内存,而且它占用的内存在程序运行过程中会一直占用,无法释放。

 

什么场景下使用Compiled模式,需要根据实际情况具体问题具体分析,一般来说,以下场景不适合使用Compiled模式:

1.对匹配效率没有要求的场景;

2.非常简单的正则表达式;

3.极少调用的方法中声明的正则表达式;

4.循环体中声明的正则表达式(除了动态生成的正则表达式,否则不要在循环体内声明正则表达式);

5.静态方法中声明的正则表达式(静态方法每次调用都需要重新编辑正则表达式,使用Compiled模式只会降低效率)。

 

RegexOptions.RightToLeft

RightToLeft改变的是正则表达式匹配的顺序,从右到左进行匹配

一个由字母组成的字符串,最长14位,要求每隔2位加一个逗号,最左边不加,求一个好的算法

例:“abcdefg”    返回“a,bc,de,fg”

代码实现:

string test = "abcdefg";

string result = Regex.Replace(test, @"(?<!^)[a-zA-Z]{2}", ",$0", RegexOptions.RightToLeft);

 

RegexOptions.ExplicitCapture

这一模式改变的是普通捕获组的匹配行为。将普通捕获组解释为非捕获组,只有显式命名的命名捕获组才当作捕获组使用。

捕获组的作用是将括号()内子表达式匹配到的内容保存到内存中一个组里,供以后引用,在.NET中捕获组有两种形式

(Expression) 普通捕获组

(?<name>Expression) 命名捕获组

其它形式的(?...)都不是捕获组。

但是(Expression)这种捕获组语法规则也带来一个副作用,在一些不得不使用()的场合,会默认为使用了捕获组,将匹配到的内容保存到内存中,而有些情况下这些内容并不需要关心的,浪费了系统资源,降低了匹配效率,所以才有了非捕获组(?:Expression)的出现,来抵消这一副作用。而非捕获组带来的另一个副作用的就是可读性的降低。

string test = "<li title=\"截至2009-07-28 20:45:49,用户的总技术分为:5988;截至2009-07-26日,用户的总技术分排名为:4133\">(...)</li>";

Regex reg = new Regex(@"([01][0-9]|2[0-3])(:[0-5][0-9]){2}", RegexOptions.ExplicitCapture);

MatchCollection mc = reg.Matches(test);

string s = "";

foreach (Match m in mc)

{

s += m.Value + "\n";

s += m.Groups[1].Value + "\n";

s += m.Groups[2].Value + "\n";

}

Console.Write(s);

未开启RegexOptions.ExplicitCapture

/*

20:45:49

20

:49

*/

开启的结果是

/*

20:45:49

 

 

*/

 

 

(?imnsx-imnsx:)形式

string[] test = new string[] { "Abc", "AbcdefGHIjklmn", "abcdefghijklmn" };

Regex reg = new Regex(@"^[A-Z](?i:[A-Z]{9,19})$");

string str = "";

foreach (string s in test)

{

str += "源字符串:" + s.PadRight(15, ' ') + "  匹配结果: " + reg.IsMatch(s) + "\n";

}

Console.WriteLine(str);

/*--------输出--------

源字符串: Abc              匹配结果: False

源字符串: AbcdefGHIjklmn   匹配结果: True

源字符串: abcdefghijklmn   匹配结果: False

*/

语法:(?-i:Expression)

这种语法规则表达为括号内的子表达式关闭忽略大小写模式。通常与全局匹配模式配合使用,表示全局为忽略大小写的,局部为严格区分大小写。

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);

string str = "";

MatchCollection mc = reg.Matches(test);

foreach (Match m in mc)

{

str += m.Value + "\n";

}

Console.WriteLine(str);

/*--------输出--------

<DIV id="TEST" class="delete">second</div>

*/

环视基础

表达式

说明

(?<=Expression)

逆序肯定环视,表示所在位置左侧能够匹配Expression

(?<!Expression)

逆序否定环视,表示所在位置左侧不能匹配Expression

(?=Expression)

顺序肯定环视,表示所在位置右侧能够匹配Expression

(?!Expression)

顺序否定环视,表示所在位置右侧不能匹配Expression

 

string str = "aa<p>one</p>bb<div>two</div>cc";

foreach (Match item in Regex.Matches(str, @"<(?!/?p\b)[^>]+>"))

{

Console.WriteLine(item.Value);

}

/*

<div>

</div>

*/

string str = "<div>a test</div><div>1</div>";

foreach (Match item in Regex.Matches(str, @"(?<=<div>)[^<]+(?=</div>)"))

{

Console.WriteLine(item.Value);

}

/*

a test

1

*/

 

double[] data = new double[] { 0, 12, 123, 1234, 12345, 123456, 1234567, 123456789, 1234567890, 12.345, 123.456, 1234.56, 12345.6789, 123456.789, 1234567.89, 12345678.9 };

string s = "";

foreach (double d in data)

{

s += "源字符串:" + d.ToString().PadRight(15) + "格式化:" + Regex.Replace(d.ToString(), @"(?<=\d)(?<!\.\d*)(?=(?:\d{3})+(?:\.\d+|$))", ",") + "\n";

}

Console.Write(s);

 

结果:

源字符串:0              格式化:0

源字符串:12             格式化:12

源字符串:123            格式化:123

源字符串:1234           格式化:1,234

源字符串:12345          格式化:12,345

源字符串:123456         格式化:123,456

源字符串:1234567        格式化:1,234,567

源字符串:123456789      格式化:123,456,789

源字符串:1234567890     格式化:1,234,567,890

源字符串:12.345         格式化:12.345

源字符串:123.456        格式化:123.456

源字符串:1234.56        格式化:1,234.56

源字符串:12345.6789     格式化:12,345.6789

源字符串:123456.789     格式化:123,456.789

源字符串:1234567.89     格式化:1,234,567.89

源字符串:12345678.9     格式化:12,345,678.9

 

实现分析:

首先根据需求可以确定是把一些特定的位置替换为“,”,接下来就是分析并找到这些位置的规律,并抽象出来以正则表达式来表示。

1、这个位置的左侧必须为数字

2、这个位置右侧到出现“.”或结尾为止,必须是数字,且数字的个数必须为3的倍数

3、这个位置左侧相隔任意个数字不能出现“.”

由以上三条,就可以完全确定这些位置,只要实现以上三条,组合一下正则表达式就可以了。

根据分析,最终匹配的结果是一个位置,所以所有子表达式都要求是零宽度。

1、是对当前所在位置左侧附加的条件,所以要用到逆序环视,因为要求必须出现,所以是肯定的,符合这一条件的子表达式即为“(?<=\d)”

2、是对当前所在位置右侧附加的条件,所以要用到顺序环视,也是要求出现,所以是肯定的,是数字,且个数为3的倍数,即“(?=(?:\d{3})*)”,到出现“.”或结尾为止,即“(?=(?:\d{3})*(?:\.|$))”

3、是对当前所在位置左侧附加的条件,所以要用到逆序环视,因为要求不能出现,所以是否定的,即“(?<!\.\d*)”

因为零宽度的子表达式是非互斥的,最后匹配的都是同一个位置,所以先后顺序是不影响最后的匹配结果的,可以任意组合,只是习惯上把逆序环视写在左侧,顺序环视写在右侧。

 

 

 

贪婪与非贪婪模式匹配

string str = "aa<div>test1</div>bb<div>test2</div>cc";

foreach (Match item in Regex.Matches(str, @"<div>.*</div>"))

{

Console.WriteLine(item.Value);

}

/*贪婪模式,很好理解,尽量多匹配

<div>test1</div>bb<div>test2</div>

*/

如果正则表达式是<div>.*?</div>

/*非贪婪模式,也叫懒惰模式,就是懒的意思,匹配到了就不会再向后匹配

<div>test1</div>

<div>test2</div>

*/

 

贪婪非贪婪的比较以及优化

string str =@"The phrase ""regular expression"" is called ""Regex"" for short.";

foreach (Match item in Regex.Matches(str, @""".*"""))

{

Console.WriteLine(item.Value);

}

一、使用”.*”这种匹配的结果不符合:"regular expression" is called "Regex"

二、使用”.*?”的结果:

"regular expression"

"Regex"

OK,不过进行了四次回溯

三、使用这种[^"]*匹配OK,没有回溯

四、对三的改进,固化分组 (?>[^"]*),匹配效率最好

 

再看一例,获取img标签的src内容:

string str =@"<img class=""test"" src=""/img/logo.gif"" title=""测试"" />";

foreach (Match item in Regex.Matches(str, @"<img\b.*?src=""(.*?)"".*?"))

{

Console.WriteLine(item.Groups[1].Value);

}

使用的是非贪婪模式,我们通过排除型字符组转换为贪婪模式,提高匹配效率

@"<img\b.*?src=""([^""]*)""[^>]*"

img与src之间的非贪婪通过顺序环视来转化

@"<img\b(?:(?!src=).)*src=""([^""]*)""[^>]*"

“(?!src=).”表示这样一个字符,从它开始,右侧不能是字符序列“src=”,而“(?:(?!src=).)*”就表示符合上面规则的字符,有0个或无限多个。这样就达到排除字符序列的目的,实现的效果同排除型字符组一样,只不过排除型字符组排除的是一个或多个字符,而这种环视结构排除的是一个或多个有序的字符序列。

 

但是以顺序环视的方式排除字符序列,由于在匹配每一个字符时,都要进行较多的判断,所以相对于非贪婪模式,是提升效率还是降低效率,要根据实际情况进行分析。对于简单的正则表达式,或是简单的源字符串,一般来说是非贪婪模式效率高些,而对于数量较大源字符串,或是复杂的正则表达式,一般来说是贪婪模式效率高些。

 

PS:一般都是贪婪模式+固化分组,当然也需要看具体情况。

 

一些正则的实践篇

清除掉iframe+javascript+css的恶意脚本

http://rczjp.cn/HTML/101210/20105010115029.html

正则截取URL网址

http://rczjp.cn/HTML/101218/20102718032702.html

常见的正则

http://rczjp.cn/HTML/081119/20082119022155.html

 

此文章来自原作者:http://blog.csdn.net/lxcnn/ 具体详情去过客的blog参看。

下面是作者写的NFA引擎匹配原理,讲解的很详细,值得参看:

http://blog.csdn.net/lxcnn/archive/2009/06/28/4304651.aspx

 

你可能感兴趣的:(正则表达式)