亚洲在线久爱草,狠狠天天香蕉网,天天搞日日干久草,伊人亚洲日本欧美

為了賬號安全,請及時綁定郵箱和手機立即綁定
已解決430363個問題,去搜搜看,總會有你想問的

Go 的正則表達式,匹配包含平衡括號的內容

Go 的正則表達式,匹配包含平衡括號的內容

Go
子衿沉夜 2023-02-28 20:20:18
所以我的用例如下:我正在解析一個 SQL 查詢,試圖獲取函數名稱和發送到該函數的各個參數。這要求我的正則表達式能夠找到名稱、左括號、內容和右括號。不幸的是,在測試時發現它有時過于貪婪,抓住額外的括號而其他時候它錯過了結束的括號。這是我在操場上的測試代碼:func getRegex(name string) string {    return fmt.Sprintf("\\$__%s\\b(?:\\((.*?\\)?)\\))?", name)}func main() {    var rawSQL = "(select min(time) from table where $__timeFilter(time))"    rgx, err := regexp.Compile(getRegex("timeFilter"))    if err != nil {        fmt.Println(err)    }    var match = rgx.FindAllStringSubmatch(rawSQL, -1)    fmt.Println(match)}舉個例子https://go.dev/play/p/4FpZblia7Ks我測試的4個案例如下:(select min(time) from table where $__timeFilter(time) ) OK(select min(time) from table where $__timeFilter(time)) NOKselect * from foo where $__timeFilter(cast(sth as timestamp)) OKselect * from foo where $__timeFilter(cast(sth as timestamp) ) NOK這是一個實時正則表達式版本https://regexr.com/700oh我來自 javascript 世界,所以從未使用過遞歸正則表達式,看起來這可能是一種情況?
查看完整描述

2 回答

?
九州編程

TA貢獻1785條經驗 獲得超4個贊

您的正則表達式似乎有兩個主要問題,其中一個比另一個更容易處理:

  1. 正則表達式天生就不擅長處理遞歸匹配,例如分組左括號和右括號,因為它們沒有內存。就您而言,我認為您已嘗試通過將自己限制在一些特定情況下來解決此問題,但正則表達式的貪婪性質在這里對您不利。

  2. 您不匹配右括號前可能有空格的情況。

這兩個問題一起導致您的正則表達式在這兩種情況下失敗,但也導致您的第一個案例匹配。

要解決此問題,您必須在將字符串發送到正則表達式之前對字符串進行一些預處理:

if strings.HasPrefix(rawSql, "(") {
    rawSql = rawSql[1:len(rawSql) - 1]
}

這將去掉任何外括號,如果沒有內存或額外的子句,正則表達式將無法忽略這些括號。

接下來,您需要修改正則表達式以處理內部函數調用和$__timeFilter調用之間可能存在空格的情況:

func getRegex(name string) string {
    return fmt.Sprintf("\\$__%s\\b(\\((.*?\\)?)\\s*\\))?", name)
}

這樣做之后,您的正則表達式應該可以工作了。您可以在此 playground 鏈接上找到完整示例。


查看完整回答
反對 回復 2023-02-28
?
FFIVE

TA貢獻1797條經驗 獲得超6個贊

盡管我最終不得不走另一條路,但我還是選擇了 Woody 的答案作為正確答案。附加的測試用例不包括某些場景,結果我還必須能夠提取括號內的參數。所以這是我的最終解決方案,我手動解析文本,找到邊界括號并提取它們之間的任何內容:


// getMacroMatches extracts macro strings with their respective arguments from the sql input given

// It manually parses the string to find the closing parenthesis of the macro (because regex has no memory)

func getMacroMatches(input string, name string) ([][]string, error) {

    macroName := fmt.Sprintf("\\$__%s\\b", name)

    matchedMacros := [][]string{}

    rgx, err := regexp.Compile(macroName)


    if err != nil {

        return nil, err

    }


    // get all matching macro instances

    matched := rgx.FindAllStringIndex(input, -1)


    if matched == nil {

        return nil, nil

    }


    for matchedIndex := 0; matchedIndex < len(matched); matchedIndex++ {

        var macroEnd = 0

        var argStart = 0

        macroStart := matched[matchedIndex][0]

        inputCopy := input[macroStart:]

        cache := make([]rune, 0)


        // find the opening and closing arguments brackets

        for idx, r := range inputCopy {

            if len(cache) == 0 && macroEnd > 0 {

                break

            }

            switch r {

            case '(':

                cache = append(cache, r)

                if argStart == 0 {

                    argStart = idx + 1

                }

            case ')':

                l := len(cache)

                if l == 0 {

                    break

                }

                cache = cache[:l-1]

                macroEnd = idx + 1

            default:

                continue

            }

        }


        // macroEnd equals to 0 means there are no parentheses, so just set it

        // to the end of the regex match

        if macroEnd == 0 {

            macroEnd = matched[matchedIndex][1] - macroStart

        }

        macroString := inputCopy[0:macroEnd]

        macroMatch := []string{macroString}


        args := ""

        // if opening parenthesis was found, extract contents as arguments

        if argStart > 0 {

            args = inputCopy[argStart : macroEnd-1]

        }

        macroMatch = append(macroMatch, args)

        matchedMacros = append(matchedMacros, macroMatch)

    }

    return matchedMacros, nil

}

游樂場鏈接:https://go.dev/play/p/-odWKMBLCBv


查看完整回答
反對 回復 2023-02-28
  • 2 回答
  • 0 關注
  • 201 瀏覽
慕課專欄
更多

添加回答

舉報

0/150
提交
取消
微信客服

購課補貼
聯系客服咨詢優惠詳情

幫助反饋 APP下載

慕課網APP
您的移動學習伙伴

公眾號

掃描二維碼
關注慕課網微信公眾號