Go言語で後方参照を用いた置換処理


## 追記 

下記の記事では、後方参照を使用する方法はないという内容になっていますが、
最新バージョンでは使用できるようになっています。

```go:replace.go
package main

import (
  "fmt"
  "regexp"
)

func main() {
  re := regexp.MustCompile("a(x*)b")
  fmt.Println(re.ReplaceAllString("-ab-axxb-", "$1"))
}
```

### 参考

http://golang.org/pkg/regexp/#Regexp.ReplaceAllString

----------

`regexp`パッケージのページをざっと確認したところ、
`\1`や`$1`などの後方参照を行う方法がないようなので、
`ReplaceAllStringFunc`関数と`FindAllStringSubmatch`関数を用いて、
置換処理を行うことにした。

`ReplaceAllStringFunc`関数は、マッチした各文字列を処理し変換後文字列を返す無名関数を設定する仕様になっている。

`FindAllStringSubmatch`関数は、正規表現中の丸括弧()で囲った文字列を返してくれる。後方参照の代替として使用。


```go:replace.go
package main

import (
        "fmt"
        "regexp"
)

func main() {
        // タグから置換するデータ
        var data = map[string]string{
                "NAME": "Tarou",
                "AGE":  "30",
        }

        // テンプレート
        template := "名前は{T_NAME}。年齢は{T_AGE}歳。{T_HOGEHOGE}"

        // 正規表現
        regex_str := "{T_([A-Z]+?)}"
        re, err := regexp.Compile(regex_str)
        if err != nil {
                panic(err)
        }

        // 置換処理用関数
        cb := func(s string) string {
                // 正規表現の「([A-Z]+?)」の部分を取得
                tname := re.FindStringSubmatch(s)
                if len(tname) == 2 {
                        // タグ名に応じてデータを置換する
                        if ret := data[tname[1]]; ret != "" {
                                return ret
                        } else {
                                return s
                        }
                }
                return s
        }

        // 置換処理
        ret := re.ReplaceAllStringFunc(template, cb)

        // 結果表示
        fmt.Println(ret)
}
```

今回の調査のために書いた`regexp`の関数サンプルと実行結果を載せておく。
基本的に、`[]bytes`型と`string`型のそれぞれに対して、同じ機能を備えた関数が用意されている。

関数名に`String`が含まれている関数は、`string`型を渡し、
含まれていない関数は、`[]byte`型を渡すようになっている。

```go:replace.go
package main

import (
        "fmt"
        "regexp"
)

func main() {
        str := "テスト1{T_NAME}テスト2{T_AGE}テスト3"
        re, err := regexp.Compile("{T_(.+?)}")
        if err != nil {
                panic(err)
        }

        fmt.Println("---------- Find ------------")
        fmt.Println(string(re.Find([]byte(str))))

        fmt.Println("---------- FindAll ------------")
        rets_findall := re.FindAll([]byte(str), -1)
        for _, ret := range rets_findall {
                fmt.Println(string(ret))
        }

        fmt.Println("---------- FindAllIndex ------------")
        rets_findallindex := re.FindAllIndex([]byte(str), -1)
        for _, ret := range rets_findallindex {
                fmt.Println(ret)
        }

        fmt.Println("---------- FindAllString ------------")
        rets_findallstring := re.FindAllString(str, -1)
        for _, ret := range rets_findallstring {
                fmt.Println(ret)
        }

        fmt.Println("---------- FindAllStringIndex ------------")
        rets_findallstringindex := re.FindAllStringIndex(str, -1)
        for _, ret := range rets_findallstringindex {
                fmt.Println(ret)
        }

        fmt.Println("---------- FindAllStringSubmatch ------------")
        rets_findallstringsubmatch := re.FindAllStringSubmatch(str, -1)
        for _, ret := range rets_findallstringsubmatch {
                fmt.Println(ret)
        }

        fmt.Println("---------- FindAllStringSubmatchIndex ------------")
        rets_findallstringsubmatchindex := re.FindAllStringSubmatchIndex(str, -1)
        for _, ret := range rets_findallstringsubmatchindex {
                fmt.Println(ret)
        }

        fmt.Println("---------- FindIndex ------------")
        rets_findindex := re.FindIndex([]byte(str))
        fmt.Println(rets_findindex)

        fmt.Println("---------- FindString ------------")
        rets_findstring := re.FindString(str)
        fmt.Println(rets_findstring)

        fmt.Println("---------- FindStringIndex ------------")
        rets_findstringindex := re.FindStringIndex(str)
        fmt.Println(rets_findstringindex)

        fmt.Println("---------- FindStringSubmatch ------------")
        rets_findstringsubmatch := re.FindStringSubmatch(str)
        fmt.Println(rets_findstringsubmatch)

        fmt.Println("---------- FindStringSubmatchIndex ------------")
        rets_findstringsubmatchindex := re.FindStringSubmatchIndex(str)
        fmt.Println(rets_findstringsubmatchindex)

        fmt.Println("---------- FindSubmatch ------------")
        rets_findsubmatch := re.FindSubmatch([]byte(str))
        fmt.Println(rets_findsubmatch)

        fmt.Println("---------- FindSubmatchIndex ------------")
        rets_findsubmatchindex := re.FindSubmatchIndex([]byte(str))
        fmt.Println(rets_findsubmatchindex)

        fmt.Println("---------- Match ------------")
        rets_match := re.Match([]byte(str))
        fmt.Println(rets_match)

        fmt.Println("---------- MatchString ------------")
        rets_matchstring := re.MatchString(str)
        fmt.Println(rets_matchstring)

        fmt.Println("---------- NumSubexp ------------")
        rets_numsubexp := re.NumSubexp()
        fmt.Println(rets_numsubexp)

        fmt.Println("---------- ReplaceAll ------------")
        rets_replaceall := re.ReplaceAll([]byte(str), []byte("REPLACE"))
        fmt.Println(string(rets_replaceall))

        fmt.Println("---------- ReplaceAllFunc ------------")
        rets_replaceallfunc := re.ReplaceAllFunc([]byte(str), func(b []byte) []byte { fmt.Println(string(b)); return []byte("REPLACE") })
        fmt.Println(string(rets_replaceallfunc))

        fmt.Println("---------- ReplaceAllString ------------")
        rets_replaceallstring := re.ReplaceAllString(str, "REPLACE")
        fmt.Println(rets_replaceallstring)

        fmt.Println("---------- ReplaceAllStringFunc ------------")
        rets_replaceallstringfunc := re.ReplaceAllStringFunc(str, func(s string) string { fmt.Println(s); return "REPLACE" })
        fmt.Println(rets_replaceallstringfunc)
}
```


```text:result
--------- Find ------------
{T_NAME}
---------- FindAll ------------
{T_NAME}
{T_AGE}
---------- FindAllIndex ------------
[10 18]
[28 35]
---------- FindAllString ------------
{T_NAME}
{T_AGE}
---------- FindAllStringIndex ------------
[10 18]
[28 35]
---------- FindAllStringSubmatch ------------
[{T_NAME} NAME]
[{T_AGE} AGE]
---------- FindAllStringSubmatchIndex ------------
[10 18 13 17]
[28 35 31 34]
---------- FindIndex ------------
[10 18]
---------- FindString ------------
{T_NAME}
---------- FindStringIndex ------------
[10 18]
---------- FindStringSubmatch ------------
[{T_NAME} NAME]
---------- FindStringSubmatchIndex ------------
[10 18 13 17]
---------- FindSubmatch ------------
[[123 84 95 78 65 77 69 125] [78 65 77 69]]
---------- FindSubmatchIndex ------------
[10 18 13 17]
---------- Match ------------
true
---------- MatchString ------------
true
---------- NumSubexp ------------
1
---------- ReplaceAll ------------
テスト1REPLACEテスト2REPLACEテスト3
---------- ReplaceAllFunc ------------
{T_NAME}
{T_AGE}
テスト1REPLACEテスト2REPLACEテスト3
---------- ReplaceAllString ------------
テスト1REPLACEテスト2REPLACEテスト3
---------- ReplaceAllStringFunc ------------
{T_NAME}
{T_AGE}
テスト1REPLACEテスト2REPLACEテスト3
```


# 参考

regexp パッケージ
http://golang.jp/pkg/regexp