NewReplacer使用技巧
原文链接:http://www.zhoubotong.site/post/34.html
上次写博客至今有段时间了,这些日子,认真过,努力过,职场中不管有哪些让人失意或不快的事,终归到底,是自己不够强大。。。
好吧,新的一年,不磨磨唧唧了,一般处理xss漏洞使用正则匹配,再次分享一个golang strings包NewReplacer的方法。
我们先看一个简单的例子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | 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。
我们再看个例子:
1 2 3 4 5 6 7 8 9 10 11 12 13 | 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会如何处理?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | 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。
无论从事什么行业,只要做好两件事就够了,一个是你的专业、一个是你的人品,专业决定了你的存在,人品决定了你的人脉,剩下的就是坚持,用善良專業和真诚赢取更多的信任。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 25岁的心里话
· 闲置电脑爆改个人服务器(超详细) #公网映射 #Vmware虚拟网络编辑器
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 零经验选手,Compose 一天开发一款小游戏!
· 一起来玩mcp_server_sqlite,让AI帮你做增删改查!!