2015-01-17 25 views
4

如何從ReplaceAllFunc()內部訪問捕獲組?如何從regexp.ReplaceAllFunc訪問捕獲組?

package main 

import (
    "fmt" 
    "regexp" 
) 

func main() { 
    body := []byte("Visit this page: [PageName]") 
    search := regexp.MustCompile("\\[([a-zA-Z]+)\\]") 

    body = search.ReplaceAllFunc(body, func(s []byte) []byte { 
     // How can I access the capture group here? 
    }) 

    fmt.Println(string(body)) 
} 

目標是與<a href="/view/PageName">PageName</a>更換[PageName]

這是Writing Web Applications Go教程底部「其他任務」部分下的最後一項任務。

回答

4

我同意在功能內部訪問捕獲組是理想的,我認爲這不可能與regexp.ReplaceAllFunc。 只有在我腦海中的東西現在如何看待與功能要做到這一點是這樣的:

package main 

import (
    "fmt" 
    "regexp" 
) 

func main() { 
    body := []byte("Visit this page: [PageName] [OtherPageName]") 
    search := regexp.MustCompile("\\[[a-zA-Z]+\\]") 
    body = search.ReplaceAllFunc(body, func(s []byte) []byte { 
     m := string(s[1 : len(s)-1]) 
     return []byte("<a href=\"/view/" + m + "\">" + m + "</a>") 
    }) 
    fmt.Println(string(body)) 
} 

編輯

還有就是我知道該怎麼做你想做的另一種方式。首先您需要知道的是,您可以使用語法(?:re)指定非捕獲組,其中re是您的正則表達式。這不是必需的,但會減少無關匹配的數量。

接下來要知道的是regexp.FindAllSubmatcheIndex。它會返回切片,其中每個內部切片表示範圍全部子匹配給定正則表達式的匹配。

有了這個兩件事情,你可以構造有些通用的解決方案:

package main 

import (
    "fmt" 
    "regexp" 
) 

func ReplaceAllSubmatchFunc(re *regexp.Regexp, b []byte, f func(s []byte) []byte) []byte { 
    idxs := re.FindAllSubmatchIndex(b, -1) 
    if len(idxs) == 0 { 
     return b 
    } 
    l := len(idxs) 
    ret := append([]byte{}, b[:idxs[0][0]]...) 
    for i, pair := range idxs { 
     // replace internal submatch with result of user supplied function 
     ret = append(ret, f(b[pair[2]:pair[3]])...) 
     if i+1 < l { 
      ret = append(ret, b[pair[1]:idxs[i+1][0]]...) 
     } 
    } 
    ret = append(ret, b[idxs[len(idxs)-1][1]:]...) 
    return ret 
} 

func main() { 
    body := []byte("Visit this page: [PageName] [OtherPageName][XYZ]  [XY]") 
    search := regexp.MustCompile("(?:\\[)([a-zA-Z]+)(?:\\])") 

    body = ReplaceAllSubmatchFunc(search, body, func(s []byte) []byte { 
     m := string(s) 
     return []byte("<a href=\"/view/" + m + "\">" + m + "</a>") 
    }) 

    fmt.Println(string(body)) 
} 
+0

是的,這是我想到的解決方法。不過,使用捕獲組將會是一個更優雅的解決方案。太糟糕了,我們不能用'ReplaceAllFunc()'來做到這一點...... –

0

您必須再次調用ReplaceAllFunc第一和函數調用FindStringSubmatch內同一正則表達式。像:

func (p parser) substituteEnvVars(data []byte) ([]byte, error) { 
    var err error 
    substituted := p.envVarPattern.ReplaceAllFunc(data, func(matched []byte) []byte { 
     varName := p.envVarPattern.FindStringSubmatch(string(matched))[1] 
     value := os.Getenv(varName) 
     if len(value) == 0 { 
      log.Printf("Fatal error substituting environment variable %s\n", varName) 
     } 

     return []byte(value) 
    }); 
    return substituted, err 
}