• 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。

      

      

      

  • 相关阅读:
    Cocos2dx开发(3)——Cocos2dx打包成APK,ANT环境搭建
    Cocos2dx开发(2)——Win8.1下Cocod2dx 3.2环境搭建
    Cocos2dx开发(1)——Win8.1下 NDK r10 环境搭建
    设计模式备忘录(1):适配器模式、依赖注入依赖倒置、空对象模式
    使用latencytop深度了解你的系统的延迟(转)
    操作系统基础
    计算机基础
    说明exit()函数作用的程序
    变量的引用类型和非引用类型的区别
    二进制转16进制JAVA代码
  • 原文地址:https://www.cnblogs.com/phpper/p/15835445.html
Copyright © 2020-2023  润新知