【问题标题】:Replace a regular expression submatch using a function使用函数替换正则表达式子匹配
【发布时间】:2013-06-12 12:25:04
【问题描述】:

假设我有像

这样的字符串
input := `bla bla b:foo="hop" blablabla b:bar="hu?"`

我想使用函数替换b:foo="hop"b:bar="hu?" 中引号之间的部分。

很容易构建一个正则表达式来获取匹配和子匹配,例如

r := regexp.MustCompile(`\bb:\w+="([^"]+)"`)

然后调用ReplaceAllStringFunc,但问题是回调接收整个匹配而不是子匹配:

fmt.Println(r.ReplaceAllStringFunc(input, func(m string) string {
    // m is the whole match here. Damn.
}))

如何替换子匹配?

现在,我没有找到比使用正则表达式在回调中分解自己 m 并在处理完子匹配后重建字符串更好的解决方案。

如果它们在 Go 中可用,我会使用另一种方法并积极看待它们,但事实并非如此(无论如何它们不应该是必需的)。

我可以在这里做什么?


编辑:这是我想简化的当前解决方案:

func complexFunc(s string) string {
   return "dbvalue("+s+")" // this could be more complex
}
func main() {
        input := `bla bla b:foo="hop" blablabla b:bar="hu?"`
        r := regexp.MustCompile(`(\bb:\w+=")([^"]+)`)
        fmt.Println(r.ReplaceAllStringFunc(input, func(m string) string {
                parts := r.FindStringSubmatch(m)
                return parts[1] + complexFunc(parts[2])
        }))
}

(playground link)

困扰我的是我必须应用正则表达式两次。这听起来不对。

【问题讨论】:

  • 如果您认为这是一个设计错误,请随时file a bug
  • @FUZxxl 我不知道这是不是设计错误。对我来说,它看起来不是一个最佳设计,但我可能会错过一个简单的一次性解决方案。如果这里的其他人对此有所了解,我可能会提交错误,但我需要意见。
  • 如果您有疑虑,提交错误几乎肯定是件好事。
  • @FUZxxl 我提交了一个问题:code.google.com/p/go/issues/detail?id=5690
  • @zanona 我认为是这样,并且像在 js 中一样使用可变参数函数作为参数确实会使其干净

标签: regex go


【解决方案1】:

我不喜欢下面的代码,但它似乎做了你希望它做的事情:

package main

import (
        "fmt"
        "regexp"
)

func main() {
        input := `bla bla b:foo="hop" blablabla b:bar="hu?"`
        r := regexp.MustCompile(`\bb:\w+="([^"]+)"`)
        r2 := regexp.MustCompile(`"([^"]+)"`)
        fmt.Println(r.ReplaceAllStringFunc(input, func(m string) string {
                return r2.ReplaceAllString(m, `"${2}whatever"`)
        }))
}

Playground


输出

bla bla b:foo="whatever" blablabla b:bar="whatever"

编辑:采取 II。


package main

import (
        "fmt"
        "regexp"
)

func computedFrom(s string) string {
        return fmt.Sprintf("computedFrom(%s)", s)
}

func main() {
        input := `bla bla b:foo="hop" blablabla b:bar="hu?"`
        r := regexp.MustCompile(`\bb:\w+="([^"]+)"`)
        r2 := regexp.MustCompile(`"([^"]+)"`)
        fmt.Println(r.ReplaceAllStringFunc(input, func(m string) string {
                match := string(r2.Find([]byte(m)))
                return r2.ReplaceAllString(m, computedFrom(match))
        }))
}

Playground


输出:

bla bla b:foo=computedFrom("hop") blablabla b:bar=computedFrom("hu?")

【讨论】:

  • 问题是我想用一个复杂的函数计算子匹配的替换(以这个子匹配作为输入),这就是为什么我不能简单地在里面使用 ReplaceAllString。至于优雅,我担心正确的解决方案会更糟:(
  • +1 用于替代解决方案,但就像我的一样,这强制使用两个正则表达式,而技术上第一个包含所有内容。
【解决方案2】:

看起来 OP 为此创建了一个问题,但截至这篇文章,仍未实现 https://github.com/golang/go/issues/5690

幸运的是,看起来网络上的其他人已经提供了他们自己的功能来执行此操作https://gist.github.com/elliotchance/d419395aa776d632d897

func ReplaceAllStringSubmatchFunc(re *regexp.Regexp, str string, repl func([]string) string) string {
    result := ""
    lastIndex := 0

    for _, v := range re.FindAllSubmatchIndex([]byte(str), -1) {
        groups := []string{}
        for i := 0; i < len(v); i += 2 {
            groups = append(groups, str[v[i]:v[i+1]])
        }

        result += str[lastIndex:v[0]] + repl(groups)
        lastIndex = v[1]
    }

    return result + str[lastIndex:]
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-05-16
    • 1970-01-01
    • 1970-01-01
    • 2015-11-30
    • 1970-01-01
    • 2013-12-06
    • 2021-04-10
    • 1970-01-01
    相关资源
    最近更新 更多