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
相关产品推荐
相关产品推荐

