看到平衡組,遞迴匹配這樣的太充滿術語性的名詞又要頭大了啊.其實簡單點講就是怎麼去匹配那些互相匹配並且互相嵌套的字元對.
比如(),[],{}這樣的配對的括弧.如果你寫代碼時某個函數很長很長,那些嵌套的{}會把你搞暈了,不知道哪個配哪個了啊.那編譯時間人家編譯器怎麼知道呢.它就是通過類似平衡組的理論去識別.還有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;
你可能奇怪在這裡為啥講這麼跟Regex無關的東東.其實不是無關,而是很相關.你用平衡組,遞迴匹配能實現的功能,也同樣能通過上面說的這種Stack來寫代碼來實現.只不過比較複雜一點.比如要加上些迴圈判斷.而Regex裡也肯定同樣用到了棧,只不過封裝了,你看不到.
平衡組,遞迴匹配
Regex裡是這樣用到棧的
(?'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