NewReplacer使用技巧

原文鏈接:http://www.zhoubotong.site/post/34.html

        上次寫博客至今有段時間了,這些日子,認真過,努力過,職場中不管有哪些讓人失意或不快的事,終歸到底,是自己不夠強大。。。

好吧,新的一年,不磨磨唧唧了,一般處理xss漏洞使用正則匹配,再次分享一個golang strings包NewReplacer的方法。

我們先看一個簡單的例子:

package main

import (
    "fmt"
    "strings"
)

func main() {
    var str= strings.NewReplacer("hello", "HELLO", "鳩摩智", "老鳩")
    s1 := str.Replace("hello")
    s2 := str.Replace("鳩摩智")
    fmt.Println(s1, s2)
}
//輸出 HELLO 老鳩

通過上面的輸出大家應該明眼看出來了。我們看下底層相關函數的應用:

// Replacer replaces a list of strings with replacements.
// It is safe for concurrent use by multiple goroutines.
type Replacer struct {
    once   sync.Once // guards buildOnce method
    r      replacer
    oldnew []string
}

// NewReplacer panics if given an odd number of arguments.
func NewReplacer(oldnew ...string) *Replacer {
    if len(oldnew)%2 == 1 {
        panic("strings.NewReplacer: odd argument count")
    }
    return &Replacer{oldnew: append([]string(nil), oldnew...)}
}

// Replace returns a copy of s with all replacements performed.
func (r *Replacer) Replace(s string) string {
    r.once.Do(r.buildOnce)
    return r.r.Replace(s)
}

代碼分析

NewReplacer() 使用提供的多組old=>new的字符串,創建並返回一個*Replacer替換程序的指針,Replace() 是返回s的所有替換進行完成後的一個拷貝。

即strings.NewReplacer()是 Golang中的函數從以前的新字符串集列表中返回了新的Replacer。

我們再看個例子:

package main

import (
    "fmt"
    "strings"
)

func main() {
    r := strings.NewReplacer("?", "?", ">", ">")
    fmt.Println(r.Replace("just do it ?, one -> two"))
    // 試下這裏輸出啥 fmt.Println(r.Replace("鳩摩智")) 
}
//原樣輸出 just do it ?, one -> two

很容易理解,是吧?這裏再說明下一個小注意事項:

替換是按照它們在目標字符串中顯示的順序進行,沒有重疊的匹配項。舊的字符串比較按參數順序進行。神馬個意思?

存在重複替換字節

既然是替換,我們肯定也會好奇如果是用重複替換項例如("a","A","a","B")GO會如何處理?

package main

import (
    "fmt"
    "strings"
)

func main() {
    var str1 = strings.NewReplacer("a", "A", "a", "B") // 前面 a->A, 後面a=>B
    s1 := str1.Replace("abc")                          // 一個個字符匹配吧
    fmt.Println(s1)                                    //Abc

    var str2 = strings.NewReplacer("a", "B", "a", "A")
    s2 := str2.Replace("abc")
    fmt.Println(s2) //Bbc

    var str3 = strings.NewReplacer("李莫愁", "小李", "李莫愁", "小李子")
    s3 := str3.Replace("你莫愁啊,不是李莫愁,像是李莫愁")
    fmt.Println(s3)
}
//Abc
//Bbc
//你莫愁啊,不是小李,像是小李

通過上面的測試實例,大家應該發現GO在處理過程中會按照第一次的替換規則爲準,也就是匹配規則不覆蓋。爲了確定 我們看下GO相關部分的源碼:

func (b *Replacer) build() replacer {
    oldnew := b.oldnew
    if len(oldnew) == 2 && len(oldnew[0]) > 1 {
        return makeSingleStringReplacer(oldnew[0], oldnew[1])
    }

    allNewBytes := true
    for i := 0; i < len(oldnew); i += 2 {
        if len(oldnew[i]) != 1 {
            return makeGenericReplacer(oldnew)
        }
        if len(oldnew[i+1]) != 1 {
            allNewBytes = false
        }
    }
    //如果都是字節替換,會進入這個if條件
    if allNewBytes {
        r := byteReplacer{}
        for i := range r {
            r[i] = byte(i)
        }
        // The first occurrence of old->new map takes precedence
        // over the others with the same old string.
        for i := len(oldnew) - 2; i >= 0; i -= 2 {
            o := oldnew[i][0]
            n := oldnew[i+1][0]
            r[o] = n
        }
        return &r
    }

    r := byteStringReplacer{toReplace: make([]string, 0, len(oldnew)/2)}
    // The first occurrence of old->new map takes precedence
    // over the others with the same old string.
    //這裏就是我們需要的地方,通過這個循環我們發現替換規則是倒序生成的
    //重複的靠前的會覆蓋靠後的
    for i := len(oldnew) - 2; i >= 0; i -= 2 {
        o := oldnew[i][0]
        n := oldnew[i+1]
        // To avoid counting repetitions multiple times.
        if r.replacements[o] == nil {
            // We need to use string([]byte{o}) instead of string(o),
            // to avoid utf8 encoding of o.
            // E. g. byte(150) produces string of length 2.
            r.toReplace = append(r.toReplace, string([]byte{o}))
        }
        r.replacements[o] = []byte(n)

    }
    return &r
}

最後特別提醒:如果給定奇數個參數,請記住NewReplacer會拋出panic。

  

  

  

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章