網站首頁 編程語言 正文
背景
工作中,當我們需要對字符串按照某個字符串切分成字符串數組數時,常用到strings.Split()
最近在使用過程中踩到了個坑,后對踩坑原因做了分析,并總結了使用string.Split
可能踩到的坑。最后寫本篇文章做復盤總結與分享
場景
當時是需要取某個結構體的某個屬性,并將其按,
切分 整體邏輯類似這樣的
type Info struct{ Ids string // Ids: 123,456 } func test3(info Info){ ids := info.Ids idList := strings.Split(ids , ",") if len(idList) < 1 { return } log.Println("ids-not-empty") // *** }
當ids = ""
時,控制臺打印了 ids-not-empty
,當時百思不得其解,按理來說應該直接走return
這個問題激發了我的好奇心,決定認真排查一下
前置
在排查之前,先大概講講 Go 中string
的基本結構
golang的string
它的運行時的數據結構位于reflect.StringHeader
type stringHeader struct { Data unsafe.Pointer Len int }
其中Data
指向數據數組的指針 ,Len
為數組的長度
排查
驗證
既然代碼中的 if
判斷為false
,那么就實際打印一下 isList
的長度看看呢
func test3(info Info){ ids := info.Ids idList := strings.Split(ids, ",") log.Printf("idList長度: [%d], idList: [%v]", len(idList), idList) for index, _ := range idList { 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]" ) func test3(info Info) { ids := info.Ids idList := strings.Split(ids, ",") getStringPtr("ids ", &ids) getStringSliceAllPtr("idList ", &idList) // *** } func getStringPtr(name string, 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) } func getStringSliceAllPtr(name string, 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
源碼有特殊處理了,那追一下源碼吧
func Split(s, sep string) []string { return genSplit(s, sep, 0, -1) }
大概讀一遍源碼能夠理清楚genSplit
思路
- 預先確定s 能夠被切分成
n
份 - 創建長度為
n
的數組 - 遍歷 s ,將每片數據放入數組中
- 返回
func genSplit(s, sep string, sepSave, n int) []string { if n == 0 { return nil } if sep == "" { return explode(s, n) } if n < 0 { // 計算 s 按照 seq 能被切成多少份 n = Count(s, sep) + 1 } a := make([]string, n) n-- i := 0 for i < n { // 定位 s里的第一個 sep 所在的位置 m := Index(s, sep) if m < 0 { break } // 放入返回的數組 a[i] = s[:m+sepSave] // 切割s s = s[m+len(sep):] i++ } a[i] = s return a[:i+1] }
那么問題應該出就出在 Count
函數中
跟進看看 count
函數會計算 s
字符串中包含了多少個 subStr
func Count(s, substr string) int { // special case if len(substr) == 0 { return utf8.RuneCountInString(s) + 1 } if len(substr) == 1 { return bytealg.CountString(s, substr[0]) } n := 0 for { i := Index(s, substr) if i == -1 { return n } 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)) // [a b c , a b c] 7 //返回7個數組元素 s = strings.Split("", ",") fmt.Println(s, len(s)) // [] 1 s = strings.Split("abc,abc", ",") fmt.Println(s, len(s)) // [abc abc] 2 s = strings.Split("abc,abc", "|") fmt.Println(s, len(s)) // [abc,abc] 1 fmt.Println(len("")) // 0 fmt.Println(len([]string{""})) // 1 str := "" fmt.Println(str[0]) // panic
總結
這次小小的踩坑其實也算是繞了一點點彎路,直接讀源碼就好了 hhhhhh
原文鏈接:https://juejin.cn/post/7102663514510065672
- 上一篇:Nginx本地配置SSL訪問的實例教程_nginx
- 下一篇:P標簽如何取消上下間隔
相關推薦
- 2022-07-09 Android同步異步任務與多線程和Handler消息處理機制
- 2022-12-10 docker編譯IJKPlayer播放器記錄詳解_docker
- 2022-08-06 ASP.NET實現Web網站本地化_實用技巧
- 2023-10-11 MP、MybatisPlus、聯表查詢、自定義sql、Constants.WRAPPER、ew (二
- 2022-07-10 uniapp創建自定義組件報錯
- 2022-10-30 使用AVFoundation實現視頻錄制詳解_IOS
- 2023-10-27 獲取html中元素的寬高
- 2023-02-27 Python?input()函數案例教程_python
- 最近更新
-
- window11 系統安裝 yarn
- 超詳細win安裝深度學習環境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優雅實現加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發現-Nac
- Spring Security之基于HttpR
- Redis 底層數據結構-簡單動態字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支