【分享】正则平衡组应用场景分析及性能优化
为了获得更好的阅读效果,可以到我的博客查看
.NET正则基础之——平衡组
声明一:本帖不是散分帖,只对找出错误,提供改进建议,进行技术讨论,阅读后给出个人见解的回复给分,其余回复不给分,请尽量看过帖子后再回复。
声明二:本帖给出的只是一些方法和思路,不是模板,我也一直不推荐把正则套模板来用。对于部分实现,认为不适合用正则来解决的朋友,请给出更优的实现,不要只是泛泛的说“这个不适合用正则来实现”。
声明三:本帖可能比较长,主要是因为偏重应用场景分析的缘故,如果对正则和平衡组感兴趣,可以先收起来慢慢看^_^
1 概述
平衡组是微软在.NET中提出的一个概念,主要是结合几种正则语法规则,提供对配对出现的嵌套结构的匹配。.NET是目前对正则支持最完备、功能最强大的语言平台之一,而平衡组正是其强大功能的外在表现,也是比较实用的文本处理功能,目前只有.NET支持,相信后续其它语言会提供支持。
平衡组可以有狭义和广义两种定义,狭义平衡组指.NET中定义的(?<Close-Open>Expression)语法,广义平衡组并不是固定的语法规则,而是几种语法规则的综合运用,我们平时所说的平衡组通常指的是广义平衡组。本文中如无特殊说明,平衡组这种简写指的是广义平衡组。
正是由于平衡组功能的强大,所以带来了一些神秘色彩,其实平衡组并不难掌握。下面就平衡组的匹配原理、应用场景以及性能调优展开讨论。
2 平衡组匹配原理
2.1 预备知识
平衡组通常是由量词,分支结构,命名捕获组,狭义平衡组,条件判断结构组成的,量词和分支结构这里不做介绍,这里只对命名捕获组,狭义平衡组和条件判断结构做下说明。
2.1.1 命名捕获组
语法:(?<name>Expression)
(?’name’Expression)
以上两种写法在..NET中是等价的,都是将“Expression”子表达式匹配到的内容,保存到以“name”命名的组里,以供后续引用。
对于命名捕获组的应用,这里不做重点介绍,只是需要澄清一点,平时使用捕获组时,一般反向引用或Group对象使用得比较多,可能会有一种误解,那就是捕获组只保留一个匹配结果,即使一个捕获组可以先后匹配多个子串,也只保留最后一个匹配到的子串。但事实是这样吗?
举例来说:
源字符串:abcdefghijkl
正则表达式:(?<chars>[a-z]{2})+
命名捕获组chars最终捕获的是什么?
string test = "abcdefghijkl";Regex reg = new Regex(@"(?<chars>[a-z]{2})+");Match m = reg.Match(test);if (m.Success){ richTextBox2.Text += "匹配结果:" + m.Value + "\n"; richTextBox2.Text += "Group:" + m.Groups["chars"].Value + "\n";}//输出匹配结果:abcdefghijklGroup:kl
string test = "abcdefghijkl";Regex reg = new Regex(@"(?<chars>[a-z]{2})+");Match m = reg.Match(test);if (m.Success){ richTextBox2.Text += "匹配结果:" + m.Value + "\n"; richTextBox2.Text += "Group:" + m.Groups["chars"].Value + "\n--------------\n"; foreach (Capture c in m.Groups["chars"].Captures) { richTextBox2.Text += "Capture:" + c + "\n"; }}//输出匹配结果:abcdefghijklGroup:kl--------------Capture:abCapture:cdCapture:efCapture:ghCapture:ijCapture:kl
string test = "abcdefghijkl";Regex reg = new Regex(@"(?<chars>[a-z]{2})");Match m = reg.Match(test);if (m.Success){ richTextBox2.Text += "匹配结果:" + m.Value + "\n"; richTextBox2.Text += "Group:" + m.Groups["chars"].Value + "\n--------------\n"; foreach (Capture c in m.Groups["chars"].Captures) { richTextBox2.Text += "Capture:" + c + "\n"; }}//输出匹配结果:abGroup:ab--------------Capture:ab
举例来说:
源字符串:abc
正则表达式:(?(?=a)\w{2}|\w)
当前位置右侧如果是字符“a” ,则匹配两个“\w”,否则匹配一个“\w”。
string test = "abc";Regex reg = new Regex(@"(?(?=a)\w{2}|\w)");MatchCollection mc = reg.Matches(test);foreach(Match m in mc){ richTextBox2.Text += m.Value + "\n";}//输出abc
string test = "a+(b*(c+d))/e+f-(g/(h-i))*j";Regex reg = new Regex(@"\(((?<Open>\()|(?<-Open>\))|[^()])*(?(Open)(?!))\)");MatchCollection mc = reg.Matches(test);foreach (Match m in mc){ richTextBox2.Text += m.Value + "\n";}//输出(b*(c+d))(g/(h-i))
Regex reg = new Regex(@"\( #普通字符“(” ( #分组构造,用来限定量词“*”修饰范围 (?<Open>\() #命名捕获组,遇到开括弧’Open’计数加1 | #分支结构 (?<-Open>\)) #狭义平衡组,遇到闭括弧’Open’计数减1 | #分支结构 [^()]+ #非括弧的其它任意字符 )* #以上子串出现0次或任意多次 (?(Open)(?!)) #判断是否还有’Open’,有则说明不配对,什么都不匹配 \) #普通闭括弧 ", RegexOptions.IgnorePatternWhitespace);
string test = "a+(b*(c+d))/e+f-(g/(h-i))*j";Regex reg0 = new Regex(@"\( #普通字符“(” ( #分组构造,用来限定量词“*”修饰范围 (?<Open>\() #命名捕获组,遇到开括弧Open计数加1 | #分支结构 (?<-Open>\)) #狭义平衡组,遇到闭括弧Open计数减1 | #分支结构 . #任意字符 )*? #以上子串出现0次或任意多次,非贪婪模式 (?(Open)(?!)) #判断是否还有'OPEN',有则说明不配对,什么都不匹配 \) #普通闭括弧 ", RegexOptions.IgnorePatternWhitespace);MatchCollection mc = reg0.Matches(test);foreach (Match m in mc){ richTextBox2.Text += m.Value + "\n";}//输出(b*(c+d))(g/(h-i))
Regex reg1 = new Regex(@"\( #普通字符“(” ( #分组构造,用来限定量词“*”修饰范围 (?<Open>\() #命名捕获组,遇到开括弧’Open’计数加1 | #分支结构 (?<-Open>\)) #狭义平衡组,遇到闭括弧’Open’计数减1 | #分支结构 [^()]+ #非括弧的其它任意字符 )* #以上子串出现0次或任意多次 (?(Open)(?!)) #判断是否还有’Open’,有则说明不配对,什么都不匹配 \) #普通闭括弧 ", RegexOptions.IgnorePatternWhitespace);
Regex reg = new Regex(@"(?is) #匹配模式,忽略大小写,“.”匹配任意字符 <div[^>]*> #开始标记“<div...>” (?> #分组构造,用来限定量词“*”修饰范围 <div[^>]*> (?<Open>) #命名捕获组,遇到开始标记,入栈,Open计数加1 | #分支结构 </div> (?<-Open>) #狭义平衡组,遇到结束标记,出栈,Open计数减1 | #分支结构 (?:(?!</?div\b).)* #右侧不为开始或结束标记的任意字符 )* #以上子串出现0次或任意多次 (?(Open)(?!)) #判断是否还有'OPEN',有则说明不配对,什么都不匹配 </div> #结束标记“</div>” ", RegexOptions.IgnorePatternWhitespace);
[解决办法]
Regex reg = new Regex(@"(?is) #匹配模式,忽略大小写,“.”匹配任意字符 <div[^>]*> #开始标记“<div...>” (?> #分组构造,用来限定量词“*”修饰范围 <div[^>]*> (?<Open>) #命名捕获组,遇到开始标记,入栈,Open计数加1 | #分支结构 </div> (?<-Open>) #狭义平衡组,遇到结束标记,出栈,Open计数减1 | #分支结构 (?:(?!</?div\b).)* #右侧不为开始或结束标记的任意字符 )* #以上子串出现0次或任意多次 (?(Open)(?!)) #判断是否还有'OPEN',有则说明不配对,什么都不匹配 </div> #结束标记“</div>” ", RegexOptions.IgnorePatternWhitespace);
[解决办法]
我是个使用正则的小菜...希望要是lz要是能够讲的更实际的作用,和Regex这个的更多知识就好了...
[解决办法]
匹配html标签比较复杂
会碰到如下情况:
<a href="javascript:alert(1 > 2)"/><a href="javascript:document.write('<b>hello</b>')"/>
[解决办法]
看得头都晕了,看来基本功还要再练
[解决办法]
看来基本功还要再练
正则是个好东西
要想用好,要有一定的内功
[解决办法]