web-dev-qa-db-ja.com

Go正規表現でグループ機能をキャプチャする方法

ライブラリをRuby=からGoにポーティングしていますが、RubyがGo(google RE2)と互換性がないことを発見しました。 Ruby&Java(他の言語はPCRE正規表現(Perl互換、グループのキャプチャをサポート)を使用))、 Goで問題なくコンパイルできるように式を書きます。

たとえば、次の正規表現があります。

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

これは次のような入力を受け入れる必要があります。

2001-01-20

キャプチャグループにより、年、月、日を変数にキャプチャできます。各グループの価値を取得するには、非常に簡単です。返された一致したデータにグループ名でインデックスを付けるだけで、値を取得できます。そのため、たとえば年を取得するには、次のような擬似コードを使用します。

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

これは表現でよく使うパターンなので、多くの書き直しが必要です。

だから、Go正規表現でこの種の機能を取得する方法はありますか?これらの式をどのように書き直す必要がありますか?

40
Plastikfan

これらの式をどのように書き直す必要がありますか?

定義されているように、いくつかのPを追加します here

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

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

私はURL表現を処理するための関数を作成しましたが、それもあなたのニーズに合っています。 this スニペットを確認できますが、次のように機能します。

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

この関数は次のように使用できます。

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

出力は次のようになります。

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

RAMおよびCPU使用率を改善するには、ループ内で匿名関数を呼び出さず、ループ内のメモリ内の配列を「追加」関数でコピーせずに、次の例を参照してください。

「+」で文字列を追加せずに、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])
}

出力:

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

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

匿名グループを使用することをお勧めします(パフォーマンスの向上)

Githubに投稿された "ReplaceAllGroupFunc"を使用するのは、次の理由から悪い考えです。

  1. ループ内でループを使用しています
  2. ループ内で匿名関数呼び出しを使用しています
  3. 多くのコードがあります
  4. ループ内で「追加」機能を使用していますが、それは悪いことです。 「追加」関数の呼び出しが行われるたびに、配列を新しいメモリ位置にコピーします
9
VasileM

グループのキャプチャ中に機能に基づいて置き換える必要がある場合は、これを使用できます。

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

例:

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

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

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

0
spiil