簡體   English   中英

控制平衡括號

[英]Controlling balanced parenthesis

假設我們有一個包含不同括號的字符串(在這種情況下,用括號括起來,我的意思是([{ ),該字符串還可以包含其他內容,例如{[balanced(parenthesis)]}但其余內容大部分將被忽略。 。可以使用正則表達式來控制所有不同的括號是:

  1. “ closed” ,即每個“ opening”括號都與“ closes”括號匹配,並且
  2. 在字符串的正確位置?

可以用下面的正則表達式來做到這一點,盡管有點難看。

這是一個匹配的正則表達式,而不是驗證正則表達式,但是您可以添加錨點,方法是在開頭和結尾添加錨點,以將其轉換為驗證正則表達式。

(?:
    (?>[^(){}\[\]]+)
    |
    (?<open>
      (?=(?<mr>\())(?<mc>)(?<ms>)\(|
      (?=(?<mc>\{))(?<mr>)(?<ms>)\{|
      (?=(?<ms>\[))(?<mc>)(?<mr>)\[
    )
    |
    (?:
        (?<-open>
          (?!\k<mc>)\}
          |
          (?!\k<mr>)\)
          |
          (?!\k<ms>)\]
        )
        (?<-mr>)(?<-mc>)(?<-ms>)
    )
)+(?(open)(?!))

由於我們無法讀取頂層堆棧,因此必須通過3個捕獲組mrmcms來模擬它。 mrmcmsopen中的項目數始終相同。 當堆棧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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM