看到平衡组,递归匹配这样的太充满术语性的名词又要头大了啊.其实简单点讲就是怎么去匹配那些互相匹配并且互相嵌套的字符对.
比如(),[],{}这样的配对的括号.如果你写代码时某个函数很长很长,那些嵌套的{}会把你搞晕了,不知道哪个配哪个了啊.那编译时人家编译器怎么知道呢.它就是通过类似平衡组的理论去识别.还有html,xml里面会有一堆<>这样的尖括号,如果不知道怎么去匹配配对的<>那肯定就乱套了.这里同样也用到平衡组类似的理论
先来讲点C#栈(Stack)的知识
那怎么才能去正确的匹配呢.很多大学课程里学数据结构这门课时可能会有这样的一个习题,就是有一个字符串里面有数字和算术操作符和括号.怎么把它解析出来当成算术表达式并得到结果.比如string str = "(3+4)*(9-5) - 2";怎么把它解析出来并算出结果.要实现这样的目的最简单的思路就是用栈这种数据结构.它是后进后出的一种结构.往栈中加入元素叫入栈,
从栈中删除元素叫出栈.假如传入字符'('则入栈,传入字符')'时不仅不入栈,还把之前的'('给删除掉.用C#的代码可以这样写
char input = '(';
Stack<char> stack = new Stack<char>();
if (input == '(')
stack.Push(input);
if (input == ')')
stack.Pop();
if (stack.Count == 0) //判断栈里面的元素是否为空
return true;
else
return false;
你可能奇怪在这里为啥讲这么跟正则表达式无关的东东.其实不是无关,而是很相关.你用平衡组,递归匹配能实现的功能,也同样能通过上面说的这种Stack来写代码来实现.只不过比较复杂一点.比如要加上些循环判断.而正则表达式里也肯定同样用到了栈,只不过封装了,你看不到.
平衡组,递归匹配
正则表达式里是这样用到栈的
(?'group'exp) 这其实就是前面讲的给一个分组命名,exp是一个字符串,用括号括起来就成一个分组了,group就是组名.只是它除了起命名的作用外,后台的操作就是把exp入栈
假如exp是左括号即(?'group'\(),则它的操作实际上和前面的代码if (input == '(') stack.Push(input); 一样
(?'-group'exp) 这里多了个-表示出栈.假如exp是右括号)即(?'-group'\))则它的操作和前面的代码if (input == ')') stack.Pop();一样.把exp出栈
(?(group)yes|no) 如果堆栈上存在以名为group的捕获内容的话,继续匹配yes部分的表达式,否则继续匹配no部分.其实这里yes和no可以去年一个
往往是去掉yes,就留下no而no也基本上不做其他操作,只是返回个表示匹配成功或失败的标志.这里用(?!)来表示匹配失败.前面有讲过零宽断言.它就是一种特殊的断言.
所以我们大部分时候的是这样用
(?(group)(?!) 它的意思和上面的代码一样.if (stack.Count == 0) //判断栈里面的元素为空
return true;
else
return false;
在这里举个比较简单的例子.要假如有一字符串.要匹配其中以第一个左尖括号后面开始出现的尖括号对<>里的内容
string source = "**<a<b<arwen>c>other>**";
string pattern = @"[^<>]* #匹配任何非尖括号的字符
(?'group'<) #匹配<并且入栈
[^<>]* #匹配任意非尖括号的字符
(?'-group'>) #匹配>并且出栈
[^<>]* #匹配任意非尖括号的字符
(?(group)(?!)) #判断栈中是否为空,为空则匹配成功,否则失败
";
Console.WriteLine(Regex.Match(source, pattern,RegexOptions.IgnorePatternWhitespace).Value);
结果是
b<arwen>c