kevin55

  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

元字符


描述


.点


匹配任何单个字符。例如正则表达式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

 

posted on 2013-10-17 23:17  kernel_main  阅读(693)  评论(0编辑  收藏  举报