摘要:看到论坛上的一个正则表达式问题,原来的编码是Ruby的,但考虑到Ruby的正则引擎类型与.NET的相仿,所以这个问题也适用于.NET。把原文篡改一下,问,
string s = "banana";Regex re = new Regex("(an)*");Match m = re.Match(s);if (m.Success){ Console.WriteLine("<<{0}>>", m.Value);}
的输出是什么?考虑一下,再继续。。。
如果你见到输出为
<<>>
而不觉得奇怪的话,你大概已经是正则大家,那么不用读下去了。
问问题的这位兄弟奇怪输出为什么不是
<<anan>>?
用他自己的话来说,“但是匹配模式应该是贪婪模式才对啊” 。
如果你在上面添加一行
Console.WriteLine("匹配的位置:{0}", m.Index);
你会发现输出是,
匹配的位置:0
匹配的是字符串的初始位置!
读过 Jeffrey E.F. Friedl 写的正则表达式经典著作《Mastering Regular Expressions》的大概知道,2条最基本的匹配规则是,
1. The match that begins earliest (leftmost) wins.2. The standard quantifiers ( * , + , ? , and {m,n} ) are greedy.
根据这第一条规则,匹配是从字符串的第一个字符的前面开始尝试的,这个尝试包括了在这个位置,对整个正则表达式的所有组合来测试,看是否符合条件,所有的组合都试过后,如果没有匹配的,就会向前移动一个字符,即从第二个字符前开始尝试。。。。(详见该书第三版中讨论正则引擎处理机制的第四章 “The Mechanics of Expression Processing”,在O'REILLY网站上可以下载到。)
再看一下上面的表达式,“(an)*”,因为是“*”,可以匹配0次或多次,所以它能够匹配字符串的这个初始位置以及任何一个没有匹配的位置!试一下
string s = "xyzbanana"; Regex re = new Regex("(an)*"); foreach(Match m in re.Matches(s)) { Console.WriteLine("在位置:{0}[{1}],匹配<<{2}>>,长度={3}", m.Index, m.Index < s.Length? s[m.Index] : '尾', m.Value, m.Value.Length); }
输出是,
在位置:0[x],匹配<<>>,长度=0在位置:1[y],匹配<<>>,长度=0在位置:2[z],匹配<<>>,长度=0在位置:3[b],匹配<<>>,长度=0在位置:4[a],匹配<<anan>>,长度=4 <----贪婪匹配在位置:8[a],匹配<<>>,长度=0在位置:9[尾],匹配<<>>,长度=0
如果表达式是“(an)+”,则结果有所不同,因为“+”匹配1次或多次,这只有在移动到an字符串之前才会匹配。。。...[
阅读全文]