web-dev-qa-db-fra.com

Comment obtenir la fonctionnalité de capture de groupe dans les expressions régulières Go

Je porte une bibliothèque de Ruby vers Go et viens de découvrir que des expressions régulières dans Ruby ne sont pas compatibles avec Go (google RE2). C'est venu à mon attention que Ruby & Java (d'autres langages utilisent des expressions régulières PCRE (compatible Perl, qui prend en charge la capture de groupes))). écris mes expressions pour qu'elles se compilent bien dans Go.

Par exemple, j'ai la regex suivante:

`(?<Year>\d{4})-(?<Month>\d{2})-(?<Day>\d{2})`

Cela devrait accepter des entrées telles que:

2001-01-20

Les groupes de capture permettent de capturer l'année, le mois et le jour en variables. Pour obtenir la valeur de chaque groupe, c'est très facile. vous indexez simplement dans les données correspondantes retournées avec le nom du groupe et vous récupérez la valeur. Donc, par exemple, pour obtenir l’année, quelque chose comme ce pseudo-code:

m=expression.Match("2001-01-20")
year = m["Year"]

C’est un motif que j’utilise beaucoup dans mes expressions, alors j’ai beaucoup de choses à réécrire.

Alors, y a-t-il un moyen d’obtenir ce genre de fonctionnalité dans Go regexp; Comment devrais-je réécrire ces expressions?

40
Plastikfan

comment devrais-je réécrire ces expressions?

Ajoutez quelques ps, comme défini ici :

(?P<Year>\d{4})-(?P<Month>\d{2})-(?P<Day>\d{2})

Renvoyez les noms des groupes de capture avec re.SubexpNames().

Et utilisez comme suit :

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())
}
56
thwd

J'avais créé une fonction permettant de gérer les expressions url, mais elle convient également à vos besoins. Vous pouvez vérifier this extrait mais cela fonctionne simplement comme ceci:

/**
 * 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
}

Vous pouvez utiliser cette fonction comme:

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

et le résultat sera:

map[Year:2015 Month:05 Day:27]
18
eluleci

Pour améliorer RAM et utilisation de la CPU sans appeler de fonctions anonymes à l'intérieur d'une boucle et sans copier des tableaux en mémoire à l'intérieur d'une boucle à l'aide de la fonction "append", voir l'exemple suivant:

Vous pouvez stocker plusieurs sous-groupes avec du texte multiligne, sans chaîne supplémentaire avec '+' et sans utiliser boucle for boucle interne (comme dans les autres exemples publiés ici).

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])
}

Sortie:

year: 2001, month: 01, day: 20
year: 2009, month: 03, day: 22
year: 2018, month: 02, day: 25
year: 2018, month: 06, day: 07

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

Si vous souhaitez stocker ces informations, utilisez un type de structure:

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]}
   }
}

Il est préférable d'utiliser des groupes anonymes (amélioration des performances).

Utiliser "ReplaceAllGroupFunc" sur Github est une mauvaise idée car:

  1. utilise la boucle dans la boucle
  2. utilise un appel de fonction anonyme dans la boucle
  3. a beaucoup de code
  4. utilise la fonction "append" dans la boucle et c'est mauvais. Chaque fois qu'un appel est fait pour "ajouter" la fonction, copie le tableau dans la nouvelle position de mémoire
9
VasileM

Si vous devez remplacer en fonction d'une fonction lors de la capture de groupes, vous pouvez utiliser ceci:

import "regexp"

func ReplaceAllGroupFunc(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:]
}

Exemple:

str := "abc foo:bar def baz:qux ghi"
re := regexp.MustCompile("([a-z]+):([a-z]+)")
result := ReplaceAllGroupFunc(re, str, func(groups []string) string {
    return groups[1] + "." + groups[2]
})
fmt.Printf("'%s'\n", result)

https://Gist.github.com/elliotchance/d419395aa776d632d897

1
Elliot Chance

Un moyen simple de déterminer les noms de groupe en fonction de la réponse @VasileM.

Avertissement: il ne s'agit pas d'optimisation mémoire/cpu/temps

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])
        }
    }
}

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

0
spiil