Go语言源码中Replacer查找部份的笔记
用过strings.NewReplacer,replacer.Replace(),它按对传入参数后,能依优先级替换,并能处理中文字符串参数.
觉得功能强大,特别好用.对它的查找和优先级怎么处理有点兴趣,花时间研究了下源码,在这记录一下个人理解.
package main //author:xcl
//2014-1-20 记录import ("fmt""strings"
)func main(){patterns := []string{ "y","25","中","国","中工","家伙",} /*patterns := make([]string,270 * 2)for i :=0;i< 270 *2;i++{ patterns[i] = fmt.Sprintf("%d",i)}*/replacer := strings.NewReplacer(patterns...)format := "中(国)--中工(家伙)"strfmt := replacer.Replace(format) NewReplacer(patterns...);fmt.Println("\nmain() replacer.Replace old=",format)fmt.Println("main() replacer.Replace new=",strfmt)
}func NewReplacer(oldnew ...string){r := makeGenericReplacer(oldnew)val,keylen,found := r.lookup("中",true)fmt.Println("\nNewReplacer() 中 val:",val," keylen:",keylen," found:",found)val,keylen,found = r.lookup("中工",true)fmt.Println("NewReplacer() 中工 val:",val," keylen:",keylen," found:",found)val,keylen,found = r.lookup("y",false)fmt.Println("NewReplacer() y val:",val," keylen:",keylen," found:",found)/*val,keylen,found := r.lookup("2",true)fmt.Println("\nNewReplacer() 2 val:",val," keylen:",keylen," found:",found)val,keylen,found = r.lookup("3",true)fmt.Println("\nNewReplacer() 3 val:",val," keylen:",keylen," found:",found)
*/
}type genericReplacer struct {root trieNode //一个字典树// tableSize is the size of a trie node's lookup table. It is the number// of unique key bytes.tableSize int// mapping maps from key bytes to a dense index for trieNode.table.mapping [256]byte
}func makeGenericReplacer(oldnew []string) *genericReplacer {r := new(genericReplacer)// Find each byte used, then assign them each an index.for i := 0; i < len(oldnew); i += 2 { //步长2. 第一个为pattern key := oldnew[i]fmt.Println("\nmakeGenericReplacer() for key=",key)//key[j]=utf8存储汉字的三个编码位置中的一个如228,则将其对应位置设置为1//即 r.mapping[228] = 1for j := 0; j < len(key); j++ {r.mapping[key[j]] = 1 fmt.Println("makeGenericReplacer() key[",j,"]=",key[j])}}for _, b := range r.mapping { r.tableSize += int(b) }fmt.Println("makeGenericReplacer() r.tableSize=",r.tableSize)var index bytefor i, b := range r.mapping {if b == 0 {r.mapping[i] = byte(r.tableSize)} else {//依数组字符编码位置,建立索引r.mapping[i] = indexfmt.Println("makeGenericReplacer() r.mapping[",i,"] =",r.mapping[i] ) index++}}// Ensure root node uses a lookup table (for performance).r.root.table = make([]*trieNode, r.tableSize) //将key,val放入字典树,注意priority=len(oldnew)-i,即越数组前面的,值越大.级别越高for i := 0; i < len(oldnew); i += 2 {r.root.add(oldnew[i], oldnew[i+1], len(oldnew)-i, r) }return r
}type trieNode struct {value stringpriority intprefix stringnext *trieNodetable []*trieNode
}func (t *trieNode) add(key, val string, priority int, r *genericReplacer) {fmt.Println("trieNode->add() val=",val," key=",key)if key == "" {if t.priority == 0 {t.value = valt.priority = priorityfmt.Println("trieNode->add() t.priority==",priority)}return}if t.prefix != "" { //处理已有前缀的node // Need to split the prefix among multiple nodes.var n int // length of the longest common prefixfor ; n < len(t.prefix) && n < len(key); n++ { //prefix与key的比较if t.prefix[n] != key[n] {break}}if n == len(t.prefix) { //相同,继续放下面t.next.add(key[n:], val, priority, r)} else if n == 0 { //没一个相同// First byte differs, start a new lookup table here. Looking up// what is currently t.prefix[0] will lead to prefixNode, and// looking up key[0] will lead to keyNode.var prefixNode *trieNodeif len(t.prefix) == 1 { //如果prefix只是一个字节的字符编码,则挂在节点下面prefixNode = t.next} else { //如果不是,将余下的新建一个trie树prefixNode = &trieNode{prefix: t.prefix[1:],next: t.next,}}keyNode := new(trieNode)t.table = make([]*trieNode, r.tableSize) //lookup()中的if node.table != nil t.table[r.mapping[t.prefix[0]]] = prefixNode t.table[r.mapping[key[0]]] = keyNode t.prefix = ""t.next = nilkeyNode.add(key[1:], val, priority, r) } else {// Insert new node after the common section of the prefix.next := &trieNode{prefix: t.prefix[n:],next: t.next,}t.prefix = t.prefix[:n]t.next = nextnext.add(key[n:], val, priority, r)}} else if t.table != nil {// Insert into existing table.m := r.mapping[key[0]]if t.table[m] == nil {t.table[m] = new(trieNode)}t.table[m].add(key[1:], val, priority, r) //构建树 } else { t.prefix = keyt.next = new(trieNode)t.next.add("", val, priority, r)}}func (r *genericReplacer) lookup(s string, ignoreRoot bool) (val string, keylen int,found bool) {// Iterate down the trie to the end, and grab the value and keylen with// the highest priority.bestPriority := 0node := &r.rootn := 0for node != nil {if node.priority > bestPriority && !(ignoreRoot && node == &r.root) {bestPriority = node.priorityval = node.valuekeylen = nfound = true}if s == "" {break}if node.table != nil {index := r.mapping[s[0]]if int(index) == r.tableSize { //字符编码第一个字节就没在table中,中断查找break}node = node.table[index] s = s[1:]n++} else if node.prefix != "" && HasPrefix(s, node.prefix) { //字符编码非第一个字节的节点会保留key在prefix中,所以通过分析prefix来继续找其它字节n += len(node.prefix)s = s[len(node.prefix):]node = node.next //继续找相同prefix以外其它字符} else {break}}return
}// HasPrefix tests whether the string s begins with prefix.
func HasPrefix(s, prefix string) bool {return len(s) >= len(prefix) && s[0:len(prefix)] == prefix
}
记录:
ascii范围内的只占一个字节,如y(121)
utf8中每个汉字占三个字节.如中(228,184,173)
构建树:
如果是新的第一个单词或词组
先进 } else if t.table != nil {
然后再进 else,这中间会把 t.prefix = key,把key值存放在prefix,将""传给下一个node
最后执行 if key == "" && t.priority == 0 { ,将 t.value = val
即key的字符编码(第一个字节)对应的root.table位置开始,依次指向另外的字符编译node,中间node的prefix存下key值.
最末一个node,存下对应的val及priority.
如果是后传入的单词或词组,先从key字符编码首个字节对应的root.table位置开始,依次查找,
} else if t.table != nil {
如果已有前缀的,进行比较 if t.prefix != "" {
1, 如目前prefix与key完全一致,则继续构建树子节点
2. 如prefix与key完全不同,则另起炉灶,构建一条新的tree
prefixNode 承上,keyNode 启下
至于为什么t.table = make([]*trieNode, r.tableSize),是为了预留映射空间.
所以它是这么弄的,而不是t.table[0],t.table[1].
t.table[r.mapping[t.prefix[0]]] = prefixNode
t.table[r.mapping[key[0]]] = keyNode
3.有部份相同, 直接跳到t.prefix[n:],然后从key[n:]开始继续构建树子节点
priority:
在这的定义是数字越大,优先级别越高
if key == "" { //字符编码中间的字节
if t.priority == 0 { //如果有定义过priority的就略过,新加的,把现有的级别加上
//对应{中,中工}这种,虽然后面有"中工",但"中",的priority要高,所以"中工"对应的值虽找到但不会返回.
if node.priority > bestPriority { bestPriority = node.priority}
例如:中工(priority=4),中(priority=2)
patterns:
"中工","家伙",
"中","国",
则:
lookup() bestPriority: 0 node.priority: 0 value: prefix:
lookup() bestPriority: 0 node.priority: 0 value: prefix: ��
lookup() bestPriority: 0 node.priority: 2 value: 国 prefix: 工
NewReplacer() 中 val: 国 keylen: 3 found: true
lookup() bestPriority: 0 node.priority: 0 value: prefix:
lookup() bestPriority: 0 node.priority: 0 value: prefix: ��
lookup() bestPriority: 0 node.priority: 2 value: 国 prefix: 工
lookup() bestPriority: 2 node.priority: 4 value: 家伙 prefix:
NewReplacer() 中工 val: 家伙 keylen: 6 found: true
main() replacer.Replace old= 中(国)--中工(家伙)
main() replacer.Replace new= 国(国)--家伙(家伙)
如果调整下顺序,把中->国提前,则会发现,下面的结果:
patterns:
"中","国",
"中工","家伙",
则:
lookup() bestPriority: 0 node.priority: 0 value: prefix:
lookup() bestPriority: 0 node.priority: 0 value: prefix: ��
lookup() bestPriority: 0 node.priority: 4 value: 国 prefix: 工
NewReplacer() 中 val: 国 keylen: 3 found: true
lookup() bestPriority: 0 node.priority: 0 value: prefix:
lookup() bestPriority: 0 node.priority: 0 value: prefix: ��
lookup() bestPriority: 0 node.priority: 4 value: 国 prefix: 工
lookup() bestPriority: 4 node.priority: 2 value: 家伙 prefix:
NewReplacer() 中工 val: 国 keylen: 3 found: true
main() replacer.Replace old= 中(国)--中工(家伙)
main() replacer.Replace new= 国(国)--国工(家伙)
还有,刚发现 lookup(s string, ignoreRoot bool) (val string, keylen int,found bool) {}中
定义在返回值中的变量,原来可以直接在函数中使用,
至于返回,直接return就行了,都不用写全返回值的,好省事.
MAIL: xcl_168@aliyun.com
BLOG:http://blog.csdn.net/xcl168
Go语言源码中Replacer查找部份的笔记相关推荐
- 【Android 逆向】ART 脱壳 ( InMemoryDexClassLoader 脱壳 | DexFile 构造函数及相关调用函数 | Android 源码中查找 native 函数 )
文章目录 一.DexFile 构造函数 二.DexFile.openInMemoryDexFile 函数 三.Android 源码中查找 native 函数 一.DexFile 构造函数 上一篇博客 ...
- 【Android 内存优化】Android 原生 API 图片压缩原理 ( 图片质量压缩方法 | 查找 Java 源码中的 native 方法对应的 C++ 源码 )
文章目录 一. 图片质量压缩方法 二. 查找对应的 Native 方法源码 三. 分析 Bitmap.cpp 中动态注册 Native 方法 在博客 [Android 内存优化]图片文件压缩 ( An ...
- 从OpenFOAM的源码中查找信息
从OpenFOAM的源码中查找信息 参考文章:https://blog.csdn.net/dsfsdffgfd/article/details/81841754
- golang中文文档_【译】Go 语言源码贡献官方指导文档
以前给 Go 语言项目源码提交过一些 commits,期间阅读他们的官方指导文档的时候觉得这篇指导文档可以作为绝佳的关于大型软件项目的规范管理的参考,因为最近又提交了几个 commits,就又把这篇文 ...
- 云豹短视频app源码中关于php--thinkcmf配置语言包的说明
云豹科技经常接到国外客户的单子,因此在语言模式设置上,需要实现灵活切换,以便不同语言的人员使用.云豹短视频app源码采用PHP做后台,Thinkcmf做框架,Thinkcmf本身是具有多语言模式的,默 ...
- 一对一视频聊天源码中的语言包应该如何开发
作为专业的一对一视频聊天源码开发服务商,与国外客户打交道是必不可少的,但是由于语言不同所以在开发和后期搭建部署时会遇到一些障碍. 除此之外还需要考虑国外客户在其所在国家上线运营时的语言问题,这就需要开 ...
- c语言strncpy,看LIBC源码中关于strncpy实现的有关问题
看LIBC源码中关于strncpy实现的问题 /* Copyright (C) 1991, 1997, 2003 Free Software Foundation, Inc. This file is ...
- Android 编译过程介绍,Android.mk 和 Android.bp 分析, 在源码中编译 AndroidStudio 构建的 App
目录 一.Android 编译 1. 编译流程 2. Soong 介绍 3. build.sh 二.Android.mk 解析 三.Android.bp 解析 1. 模块类型 2. 模块属性 四.An ...
- QQ自动点赞PHP源码_QQ好友批量回名片赞工具-QQ自动点赞附易语言源码V1.0电脑版PC端回赞-西西软件下载...
QQ自动点赞附易语言源码是一款电脑版QQ好友批量回名片赞工具下载附易语言源码和导入QQ号码方,PC端QQ回赞和源码,然后打开软件导入QQ号码即可批量赞了,每个好友可以赞10次,同时附带了源码可以编译成 ...
- Go语言源码分析CAS的实现和Java如出一辙
看了Go的源码CAS这块实现和java还是类似的. 关于Java的分析参考:Java使用字节码和汇编语言同步分析volatile,synchronized的底层实现 都是使用汇编指令:LOCK+CMP ...
最新文章
- 怎样快速学习React
- [ Python ] PIL
- Vue中绑定值与字符串拼接以及结合三目表达式实现是否为空判定的使用
- java permgen内存泄漏问题处理
- 构造函数还是静态工厂方法?
- 第14篇:Flowable-BPMN操作流程之任务完成
- 关于深圳城中村小产权房,你应该知道的
- mysql查询两个表中的不同数据_如何实现大数据在多工作表中精准查询
- 3.第一本 docker 书 --- docker 网络
- MySQL主从数据同步延时分析
- 高校就业管理系统mysql_数据库课程设计---大学生就业管理系统数据库设计.pdf
- flutter Android混淆
- 洛雪音乐助手 0.18.2 — 音乐下载工具
- Swift TextView输入字数限制
- 调用Android原生裁剪方式裁剪图片并保存
- B+树数据库加锁历史
- NumPy 学习 第三篇:矢量化和广播
- 递四方(4PX)一面
- 需求评审会议如何召开
- C#检查图片是否空白图片