[英]Controlling balanced parenthesis
假設我們有一個包含不同括號的字符串(在這種情況下,用括號括起來,我的意思是(
, [
和{
),該字符串還可以包含其他內容,例如{[balanced(parenthesis)]}
但其余內容大部分將被忽略。 。可以使用正則表達式來控制所有不同的括號是:
可以用下面的正則表達式來做到這一點,盡管有點難看。
這是一個匹配的正則表達式,而不是驗證正則表達式,但是您可以添加錨點,方法是在開頭和結尾添加錨點,以將其轉換為驗證正則表達式。
(?:
(?>[^(){}\[\]]+)
|
(?<open>
(?=(?<mr>\())(?<mc>)(?<ms>)\(|
(?=(?<mc>\{))(?<mr>)(?<ms>)\{|
(?=(?<ms>\[))(?<mc>)(?<mr>)\[
)
|
(?:
(?<-open>
(?!\k<mc>)\}
|
(?!\k<mr>)\)
|
(?!\k<ms>)\]
)
(?<-mr>)(?<-mc>)(?<-ms>)
)
)+(?(open)(?!))
由於我們無法讀取頂層堆棧,因此必須通過3個捕獲組mr
, mc
和ms
來模擬它。 mr
, mc
, ms
和open
中的項目數始終相同。 當堆棧open
不為空時,3個捕獲組中只有一個包含相應的開括號,其他2個捕獲空字符串。 非空字符串捕獲組始終是堆棧頂部的括號類型。
這使我們可以通過斷言相應的捕獲組無法匹配來匹配相應的右括號,例如(?!\\k<mc>)\\}
。 我們知道該組不能為空(因為它必須首先通過對(?<-open>)
的檢查)。 僅剩2種情況:
事實證明,此問題已由Kobi在其博客中解決。 為了簡單地平衡不同類型的括號,解決方案非常簡單而優雅,並且沒有這個問題的答案那么瘋狂,后者的語法更加復雜。
讓我引用以下博客文章的重要部分:
[...]誰說我必須將匹配的東西壓入堆棧? 如果想將任意字符串推入堆棧怎么辦? 當我看到一個開放式括號時,我真的想按一個封閉式括號,但是我該怎么辦?
訣竅是使用預讀:找到我要推送的字符的下一次出現,然后推送:
{(?=.*?(<Stack>}))
接下來,當我要匹配一個右括號時,我已經在堆棧中找到了正確的括號。 使用此方法,這是一個正則表達式,用於匹配帶有3種不同類型的匹配平衡括號的令牌:
( [^(){}\\[\\]]+ | \\( (?=[^)]* (?<Stack> \\) ) ) | \\[ (?=[^\\]]* (?<Stack> \\] ) ) | \\{ (?=[^}]* (?<Stack> \\} ) ) | \\k<Stack> (?<-Stack>) )+? (?(Stack) (?!))
當然,這種方法確實有局限性-您可能找不到要推送的角色(這可能是一件好事-它使您可以盡早失敗)。 如果您想平衡比常量字符串更復雜的內容,這也變得非常棘手,但這是另一個主題。
不,您剛剛描述的要求的語言不是常規語言,因此正則表達式不適合解決該問題。 您不僅需要使用正則表達式,還需要使用更強大的詞法分析工具。
如果您想做的只是匹配括號,請嘗試以下代碼:
public class Program
{
public static void Main()
{
string testString1 = "{[balanced(parenthesis)]}";
string testString2 = "(test)[wrong bracket type)";
string testString3 = "(test)[Mismatched]((sdff)";
bool isValid1 = ValidateString(testString1);
bool isValid2 = ValidateString(testString2);
bool isValid3 = ValidateString(testString3);
if (isValid1)
Console.WriteLine("TestString1 is balanced correctly!");
else Console.WriteLine("TestString1 is NOT balanced properly!");
if (isValid2)
Console.WriteLine("TestString2 is balanced correctly!");
else Console.WriteLine("TestString2 is NOT balanced properly!");
if (isValid3)
Console.WriteLine("TestString3 is balanced correctly!");
else Console.WriteLine("TestString3 is NOT balanced properly!");
}
public static bool ValidateString(string testString)
{
int p1 = 0;
int p2 = 0;
int p3 = 0;
var lastOpener = new Stack<char>();
foreach (char c in testString)
{
if (c == '(') {
p1++;
lastOpener.Push(c);
}
if (c == '[') {
p2++;
lastOpener.Push(c);
}
if (c == '{') {
p3++;
lastOpener.Push(c);
}
try {
if (c == ')' && lastOpener.Pop() == '(')
p1--;
if (c == ']' && lastOpener.Pop() == '[')
p2--;
if (c == '}' && lastOpener.Pop() == '{')
p3--;
} catch { return false; }
}
if (p1 != 0 || p2 != 0 || p3 != 0)
return false;
return true;
}
}
您需要做的就是調用ValidateString()
方法,並將要測試的字符串傳遞給該方法。 它將測試不匹配的括號( []
, ()
, {}
3種()
,並進行測試以確保括號在正確的位置閉合(如您在我的3個測試字符串中所見)。 如果有效,它將返回true
,否則返回false
。
該函數的工作方式是首先創建一個Stack
對象。 遍歷字符串中的每個字符。 如果找到打開的括號,則將該括號推入堆棧,並將該括號的計數器加一。 如果找到了右括號,則將其從堆棧中彈出,如果它們匹配,則會減少括號計數器。
遍歷每個字符后,它將測試每種不同類型括號的計數器,如果所有三個都為0
,那么我們知道其平衡/匹配正確!
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.