热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

Golang的strings.Split()坑怎么解决

这篇文章主要介绍“Golang的strings.Split()坑怎么解决”,在日常操作中,相信很多人在Golang的strings.Split()坑怎么解决问题上存在疑惑,小编

这篇文章主要介绍“Golang的strings.Split()坑怎么解决”,在日常操作中,相信很多人在Golang的strings.Split()坑怎么解决问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”Golang的strings.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")
       // ***
    }

    Golang的strings.Split()坑怎么解决

    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])
        }    
       // ***
    }

    Golang的strings.Split()坑怎么解决

    打印底层信息

    好奇心加深,打印一下idsidList的信息

    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)
    }

    Golang的strings.Split()坑怎么解决

    追源码

    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

    到此,关于“Golang的strings.Split()坑怎么解决”的学习就结束了,希望能够解决大家的疑惑。理论与实践的搭配能更好的帮助大家学习,快去试试吧!若想继续学习更多相关知识,请继续关注编程笔记网站,小编会继续努力为大家带来更多实用的文章!


    推荐阅读
    • Iamtryingtomakeaclassthatwillreadatextfileofnamesintoanarray,thenreturnthatarra ... [详细]
    • CSS3选择器的使用方法详解,提高Web开发效率和精准度
      本文详细介绍了CSS3新增的选择器方法,包括属性选择器的使用。通过CSS3选择器,可以提高Web开发的效率和精准度,使得查找元素更加方便和快捷。同时,本文还对属性选择器的各种用法进行了详细解释,并给出了相应的代码示例。通过学习本文,读者可以更好地掌握CSS3选择器的使用方法,提升自己的Web开发能力。 ... [详细]
    • 本文探讨了C语言中指针的应用与价值,指针在C语言中具有灵活性和可变性,通过指针可以操作系统内存和控制外部I/O端口。文章介绍了指针变量和指针的指向变量的含义和用法,以及判断变量数据类型和指向变量或成员变量的类型的方法。还讨论了指针访问数组元素和下标法数组元素的等价关系,以及指针作为函数参数可以改变主调函数变量的值的特点。此外,文章还提到了指针在动态存储分配、链表创建和相关操作中的应用,以及类成员指针与外部变量的区分方法。通过本文的阐述,读者可以更好地理解和应用C语言中的指针。 ... [详细]
    • 本文介绍了设计师伊振华受邀参与沈阳市智慧城市运行管理中心项目的整体设计,并以数字赋能和创新驱动高质量发展的理念,建设了集成、智慧、高效的一体化城市综合管理平台,促进了城市的数字化转型。该中心被称为当代城市的智能心脏,为沈阳市的智慧城市建设做出了重要贡献。 ... [详细]
    • 向QTextEdit拖放文件的方法及实现步骤
      本文介绍了在使用QTextEdit时如何实现拖放文件的功能,包括相关的方法和实现步骤。通过重写dragEnterEvent和dropEvent函数,并结合QMimeData和QUrl等类,可以轻松实现向QTextEdit拖放文件的功能。详细的代码实现和说明可以参考本文提供的示例代码。 ... [详细]
    • android listview OnItemClickListener失效原因
      最近在做listview时发现OnItemClickListener失效的问题,经过查找发现是因为button的原因。不仅listitem中存在button会影响OnItemClickListener事件的失效,还会导致单击后listview每个item的背景改变,使得item中的所有有关焦点的事件都失效。本文给出了一个范例来说明这种情况,并提供了解决方法。 ... [详细]
    • 本文介绍了Redis的基础数据结构string的应用场景,并以面试的形式进行问答讲解,帮助读者更好地理解和应用Redis。同时,描述了一位面试者的心理状态和面试官的行为。 ... [详细]
    • Java容器中的compareto方法排序原理解析
      本文从源码解析Java容器中的compareto方法的排序原理,讲解了在使用数组存储数据时的限制以及存储效率的问题。同时提到了Redis的五大数据结构和list、set等知识点,回忆了作者大学时代的Java学习经历。文章以作者做的思维导图作为目录,展示了整个讲解过程。 ... [详细]
    • 本文主要解析了Open judge C16H问题中涉及到的Magical Balls的快速幂和逆元算法,并给出了问题的解析和解决方法。详细介绍了问题的背景和规则,并给出了相应的算法解析和实现步骤。通过本文的解析,读者可以更好地理解和解决Open judge C16H问题中的Magical Balls部分。 ... [详细]
    • 本文讨论了一个关于cuowu类的问题,作者在使用cuowu类时遇到了错误提示和使用AdjustmentListener的问题。文章提供了16个解决方案,并给出了两个可能导致错误的原因。 ... [详细]
    • Oracle分析函数first_value()和last_value()的用法及原理
      本文介绍了Oracle分析函数first_value()和last_value()的用法和原理,以及在查询销售记录日期和部门中的应用。通过示例和解释,详细说明了first_value()和last_value()的功能和不同之处。同时,对于last_value()的结果出现不一样的情况进行了解释,并提供了理解last_value()默认统计范围的方法。该文对于使用Oracle分析函数的开发人员和数据库管理员具有参考价值。 ... [详细]
    • 计算机存储系统的层次结构及其优势
      本文介绍了计算机存储系统的层次结构,包括高速缓存、主存储器和辅助存储器三个层次。通过分层存储数据可以提高程序的执行效率。计算机存储系统的层次结构将各种不同存储容量、存取速度和价格的存储器有机组合成整体,形成可寻址存储空间比主存储器空间大得多的存储整体。由于辅助存储器容量大、价格低,使得整体存储系统的平均价格降低。同时,高速缓存的存取速度可以和CPU的工作速度相匹配,进一步提高程序执行效率。 ... [详细]
    • Android JSON基础,音视频开发进阶指南目录
      Array里面的对象数据是有序的,json字符串最外层是方括号的,方括号:[]解析jsonArray代码try{json字符串最外层是 ... [详细]
    • 前景:当UI一个查询条件为多项选择,或录入多个条件的时候,比如查询所有名称里面包含以下动态条件,需要模糊查询里面每一项时比如是这样一个数组条件:newstring[]{兴业银行, ... [详细]
    • 本文讨论了如何使用IF函数从基于有限输入列表的有限输出列表中获取输出,并提出了是否有更快/更有效的执行代码的方法。作者希望了解是否有办法缩短代码,并从自我开发的角度来看是否有更好的方法。提供的代码可以按原样工作,但作者想知道是否有更好的方法来执行这样的任务。 ... [详细]
    author-avatar
    lingdong369
    一个脱离低级趣味的高级动物。
    PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
    Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有