Golang的strings.Split()坑怎么解决
导读:本文共2895字符,通常情况下阅读需要10分钟。同时您也可以点击右侧朗读,来听本文内容。按键盘←(左) →(右) 方向键可以翻页。
摘要: 场景当时是需要取某个结构体的某个属性,并将其按,切分 整体逻辑类似这样的typeInfostruct{Idsstring//Ids:123,456}functest3(infoInfo){ids:=info.IdsidList:=strings.Split(ids,",")iflen(idList)<1{return}... ...
目录
(为您整理了一些要点),点击可以直达。场景
当时是需要取某个结构体的某个属性,并将其按,
切分 整体逻辑类似这样的
typeInfostruct{Idsstring//Ids:123,456}functest3(infoInfo){ids:=info.IdsidList:=strings.Split(ids,",")iflen(idList)<1{return}log.Println("ids-not-empty")//***}
当ids = ""
时,控制台打印了 ids-not-empty
,当时百思不得其解,按理来说应该直接走return
这个问题激发了我的好奇心,决定认真排查一下
前置
在排查之前,先大概讲讲 Go 中string
的基本结构
golang的string
它的运行时的数据结构位于reflect.StringHeader
typestringHeaderstruct{Dataunsafe.PointerLenint}
其中Data
指向数据数组的指针 ,Len
为数组的长度
排查
验证
既然代码中的 if
判断为false
,那么就实际打印一下 isList
的长度看看呢
functest3(infoInfo){ids:=info.IdsidList:=strings.Split(ids,",")log.Printf("idList长度:[%d],idList:[%v]",len(idList),idList)forindex,_:=rangeidList{log.Printf("idList[%d]:[%v]",index,idList[index])}//***}
打印底层信息
好奇心加深,打印一下ids
和idList
的信息
const(basePrintInfoV3="%s字符串的指针地址:[%v],字符串buf数组地址:[%v],Len字段的地址:[%p],Len字段值:[%v]"basePrintInfoV2="%s切片的指针地址:[%p],切片数组地址:[%p],Len字段的地址:[%p],Len字段的值:[%v]")functest3(infoInfo){ids:=info.IdsidList:=strings.Split(ids,",")getStringPtr("ids",&ids)getStringSliceAllPtr("idList",&idList)//***}funcgetStringPtr(namestring,str*string){s2:=(*reflect.StringHeader)(unsafe.Pointer(str))log.Printf(basePrintInfoV3,name,unsafe.Pointer(str),unsafe.Pointer(s2.Data),unsafe.Pointer(&s2.Len),s2.Len)}funcgetStringSliceAllPtr(namestring,s1*[]string){s2:=(*reflect.StringHeader)(unsafe.Pointer(s1))log.Printf(basePrintInfoV2,name,unsafe.Pointer(&s1),unsafe.Pointer(s2.Data),unsafe.Pointer(&s2.Len),s2.Len)}
追源码
ids
经过 split
之后的数组和预期的不一样,看来应该是 split
源码有特殊处理了,那追一下源码吧
funcSplit(s,sepstring)[]string{returngenSplit(s,sep,0,-1)}
大概读一遍源码能够理清楚genSplit
思路
预先确定s 能够被切分成
n
份创建长度为
n
的数组遍历 s ,将每片数据放入数组中
返回
funcgenSplit(s,sepstring,sepSave,nint)[]string{ifn==0{returnnil}ifsep==""{returnexplode(s,n)}ifn<0{//计算s按照seq能被切成多少份n=Count(s,sep)+1}a:=make([]string,n)n--i:=0fori<n{//定位s里的第一个sep所在的位置m:=Index(s,sep)ifm<0{break}//放入返回的数组a[i]=s[:m+sepSave]//切割ss=s[m+len(sep):]i++}a[i]=sreturna[:i+1]}
那么问题应该出就出在 Count
函数中
跟进看看 count
函数会计算 s
字符串中包含了多少个 subStr
funcCount(s,substrstring)int{//specialcaseiflen(substr)==0{returnutf8.RuneCountInString(s)+1}iflen(substr)==1{returnbytealg.CountString(s,substr[0])}n:=0for{i:=Index(s,substr)ifi==-1{returnn}n++s=s[i+len(substr):]}}
Count
中会走 len(substr) == 1
这个逻辑,其中的CountString
计算s
中存在多少个 substr[0]
,当时跟进,返回的结果是0
,这里符合预期 。
再结合 genSplit
中的 n = Count() + 1
我们可以发现,在genSplit
时,预先创建的数组长度就为0 + 1 = 1
! 问题迎刃而解
类似情况
经过查阅,这里再总结一下其他使用strings.Split
可能遇到的坑
s:=strings.Split("","")fmt.Println(s,len(s))//[]0//返回空数组s=strings.Split("abc,abc","")fmt.Println(s,len(s))//[abc,abc]7//返回7个数组元素s=strings.Split("",",")fmt.Println(s,len(s))//[]1s=strings.Split("abc,abc",",")fmt.Println(s,len(s))//[abcabc]2s=strings.Split("abc,abc","|")fmt.Println(s,len(s))//[abc,abc]1fmt.Println(len(""))//0fmt.Println(len([]string{""}))//1str:=""fmt.Println(str[0])//panic
</div> <div class="zixun-tj-product adv-bottom"></div> </div> </div> <div class="prve-next-news">
Golang的strings.Split()坑怎么解决的详细内容,希望对您有所帮助,信息来源于网络。