如何在Golang正则表达式中捕获组功能?

Pla*_*fan 41 regex go capture-group

我正在将一个库从Ruby移植到Go,并且刚刚发现Ruby中的正则表达式与Go(谷歌RE2)不兼容.我注意到Ruby和Java(以及其他语言使用PCRE正则表达式(perl兼容,它支持捕获组)),因此我需要重新编写表达式,以便它们在Go中编译好.

例如,我有以下正则表达式:

`(?<Year>\d{4})-(?<Month>\d{2})-(?<Day>\d{2})`
Run Code Online (Sandbox Code Playgroud)

这应该接受如下输入:

2001-01-20
Run Code Online (Sandbox Code Playgroud)

捕获组允许将年,月和日捕获到变量中.为了获得每个组的价值,这很容易; 您只需使用组名索引返回的匹配数据,然后返回值.所以,例如获得年份,像这样的伪代码:

m=expression.Match("2001-01-20")
year = m["Year"]
Run Code Online (Sandbox Code Playgroud)

这是我在表达式中使用很多的模式,所以我需要做很多重写.

那么,有没有办法在Go regexp中获得这种功能; 我该如何重写这些表达式?

thw*_*hwd 58

我该如何重写这些表达式?

添加一些诗篇,如定义在这里:

(?P<Year>\d{4})-(?P<Month>\d{2})-(?P<Day>\d{2})
Run Code Online (Sandbox Code Playgroud)

交叉引用捕获组名称re.SubexpNames().

使用方法如下:

package main

import (
    "fmt"
    "regexp"
)

func main() {
    r := regexp.MustCompile(`(?P<Year>\d{4})-(?P<Month>\d{2})-(?P<Day>\d{2})`)
    fmt.Printf("%#v\n", r.FindStringSubmatch(`2015-05-27`))
    fmt.Printf("%#v\n", r.SubexpNames())
}
Run Code Online (Sandbox Code Playgroud)

  • 哇,这就像是关于命名捕获组的最愚蠢的方法.可能只有VB可以超越它,但不是很大.命名捕获组的全部意义在于,您不必*知道它们在正则表达式中的位置,这就是为什么它们首先被引入的原因. (41认同)
  • 好吧,这看起来很令人鼓舞,但是我如何才能访问个人值、年、月和日呢? (8认同)
  • @VladimirBauer我不确定你得到了什么.我知道它并不特定于Go,我认为特别是在Go中,这个功能的内置库实现很糟糕,因为它复制了这个库的另一个更简单的特性,但是带有一个额外的无意义的语法元素. (2认同)

elu*_*eci 21

我创建了一个处理url表达式的函数,但它也适合你的需求.您可以检查代码段,但它的工作原理如下:

/**
 * Parses url with the given regular expression and returns the 
 * group values defined in the expression.
 *
 */
func getParams(regEx, url string) (paramsMap map[string]string) {

    var compRegEx = regexp.MustCompile(regEx)
    match := compRegEx.FindStringSubmatch(url)

    paramsMap = make(map[string]string)
    for i, name := range compRegEx.SubexpNames() {
        if i > 0 && i <= len(match) {
            paramsMap[name] = match[i]
        }
    }
    return
}
Run Code Online (Sandbox Code Playgroud)

您可以使用此功能,如:

params := getParams(`(?P<Year>\d{4})-(?P<Month>\d{2})-(?P<Day>\d{2})`, `2015-05-27`)
fmt.Println(params)
Run Code Online (Sandbox Code Playgroud)

输出将是:

map[Year:2015 Month:05 Day:27]
Run Code Online (Sandbox Code Playgroud)


Vas*_*leM 11

要在不调用循环内部的匿名函数的情况下提高RAM和CPU使用率,而不使用"append"函数复制内部循环内的数组,请参阅下一个示例:

您可以使用多行文本存储多个子组,而不使用'+'附加字符串,也不使用for循环for for循环(就像此处发布的其他示例一样).

txt := `2001-01-20
2009-03-22
2018-02-25
2018-06-07`

regex := *regexp.MustCompile(`(?s)(\d{4})-(\d{2})-(\d{2})`)
res := regex.FindAllStringSubmatch(txt, -1)
for i := range res {
    //like Java: match.group(1), match.gropu(2), etc
    fmt.Printf("year: %s, month: %s, day: %s\n", res[i][1], res[i][2], res[i][3])
}
Run Code Online (Sandbox Code Playgroud)

输出:

year: 2001, month: 01, day: 20
year: 2009, month: 03, day: 22
year: 2018, month: 02, day: 25
year: 2018, month: 06, day: 07
Run Code Online (Sandbox Code Playgroud)

注意:res [i] [0] = ~match.group(0)Java

如果要存储此信息,请使用结构类型:

type date struct {
  y,m,d int
}
...
func main() {
   ...
   dates := make([]date, 0, len(res))
   for ... {
      dates[index] = date{y: res[index][1], m: res[index][2], d: res[index][3]}
   }
}
Run Code Online (Sandbox Code Playgroud)

最好使用匿名组(性能改进)

使用Github上发布的"ReplaceAllGroupFunc"是个坏主意,因为:

  1. 正在使用循环内部循环
  2. 在循环中使用匿名函数调用
  3. 有很多代码
  4. 在循环中使用"append"函数,这很糟糕.每次调用"追加"功能时,都会将数组复制到新的存储位置


spi*_*iil 6

根据@VasileM 答案确定组名的简单方法。

免责声明:这与内存/CPU/时间优化无关

package main

import (
    "fmt"
    "regexp"
)

func main() {
    r := regexp.MustCompile(`^(?P<Year>\d{4})-(?P<Month>\d{2})-(?P<Day>\d{2})$`)

    res := r.FindStringSubmatch(`2015-05-27`)
    names := r.SubexpNames()
    for i, _ := range res {
        if i != 0 {
            fmt.Println(names[i], res[i])
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

https://play.golang.org/p/Y9cIVhMa2pU


Ang*_*ala 5

从 GO 1.15 开始,您可以使用Regexp.SubexpIndex. 您可以在https://golang.org/doc/go1.15#regexp查看发行说明。

根据您的示例,您将有以下内容:

re := regexp.MustCompile(`(?P<Year>\d{4})-(?P<Month>\d{2})-(?P<Day>\d{2})`)
matches := re.FindStringSubmatch("Some random date: 2001-01-20")
yearIndex := re.SubexpIndex("Year")
fmt.Println(matches[yearIndex])
Run Code Online (Sandbox Code Playgroud)

您可以在https://play.golang.org/p/ImJ7i_ZQ3Hu检查并执行此示例。