You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Go语言匹配含平衡括号内容的正则表达式问题求助

问题:解析SQL中带嵌套括号的函数调用,解决正则匹配异常问题

我需要解析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)
}

测试场景结果

(select min(time) from table where $__timeFilter(time) ) OK
(select min(time) from table where $__timeFilter(time)) NOK
select * from foo where $__timeFilter(cast(sth as timestamp)) OK
select * from foo where $__timeFilter(cast(sth as timestamp) ) NOK

解决方案

首先明确:Go标准库regexp不支持递归正则语法(比如JS ES2018+的(?R)),所以不能直接用递归正则解决嵌套括号问题,推荐两种可行方案:

1. 手动实现括号平衡解析(最可靠)

通过计数括号的方式定位匹配的闭合括号,完美支持任意层级嵌套,同时兼容括号前后的空格:

import "strings"

func extractFunctionArgs(sql string, funcName string) []string {
    funcPrefix := "$__" + funcName
    var results []string
    start := 0

    for {
        // 定位函数前缀位置
        funcPos := strings.Index(sql[start:], funcPrefix)
        if funcPos == -1 {
            break
        }
        funcStart := start + funcPos + len(funcPrefix)

        // 跳过空格,找到左括号
        leftParen := strings.Index(sql[funcStart:], "(")
        if leftParen == -1 {
            start = funcStart
            continue
        }
        parenStart := funcStart + leftParen + 1

        // 计数括号,找到匹配的右括号
        parenCount := 1
        argEnd := -1
        for i := parenStart; i < len(sql); i++ {
            switch sql[i] {
            case '(':
                parenCount++
            case ')':
                parenCount--
                if parenCount == 0 {
                    argEnd = i
                    break
                }
            }
        }

        if argEnd != -1 {
            // 提取参数并去除前后空格
            arg := strings.TrimSpace(sql[parenStart:argEnd])
            results = append(results, arg)
        }
        start = argEnd + 1
    }
    return results
}

func main() {
    testCases := []string{
        "(select min(time) from table where $__timeFilter(time) )",
        "(select min(time) from table where $__timeFilter(time))",
        "select * from foo where $__timeFilter(cast(sth as timestamp))",
        "select * from foo where $__timeFilter(cast(sth as timestamp) )",
    }
    for _, sql := range testCases {
        args := extractFunctionArgs(sql, "timeFilter")
        fmt.Printf("SQL: %s\n提取的参数: %v\n\n", sql, args)
    }
}

2. 有限层级嵌套的正则方案(仅适用于固定层级场景)

如果你的SQL中函数参数的嵌套括号层级固定(比如最多1层),可以用多层嵌套的正则表达式,缺点是扩展性差:

func getRegex(name string) string {
    // 支持1层嵌套括号,同时兼容括号前后空格
    return fmt.Sprintf(`\$__%s\b\s*\((\s*(?:[^()]|\([^()]*\))*\s*)\)`, name)
}

内容的提问来源于stack exchange,提问作者sasklacz

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.16 15:20:33