一文详解Go语言字符串

文章目录

Go语言字符串的本质

Go 语言字符串本质是指向一串一段只读的 UTF-8 字节序列的描述符

两个关键

  • 只读
  • UTF-8 编码的字节序列

内存结构

在 Go 的源码中(具体可见 runtime/string.go)

复制代码
type stringStruct struct {  
    str unsafe.Pointer  
    len int  
}
层面 原因 说明
UTF-8 编码 变长结构,无法安全修改部分字节 改动一个字节可能破坏整个字符
Go 语言设计 字符串不可变 保证安全、高效、可共享
修改方式 转为 []rune[]byte 才能进行内容更改

和其他类型的转换(内存结构的变化)

转[]rune

Unicode 规定"编号",UTF 编码规定"存法"。

Unicode 像身份证号体系,UTF 像不同格式的数据库存储规则。

转换方向 语法 说明
string → []rune runes := []rune(str) 将字符串解码成 Unicode 码点切片
[]rune → string str := string(runes) 将 Unicode 码点重新编码成 UTF-8 字符串
string → []rune 实现的具体逻辑见 func stringtoslicerune(buf *[tmpStringBufSize]rune, s string) []rune
内容 string []rune 备注
每个字符占用 1~4 字节(UTF-8) 4 字节(int32) rune 固定 4 字节
是否分配新内存 ✅ 是 转换时分配新切片
可否修改 ✅ 可以 string 只读
是否引用同一底层数据 ❌ 否 ❌ 否 完全新分配
go 复制代码
func stringtoslicerune(buf *[tmpStringBufSize]rune, s string) []rune {  
    // two passes.  
    // unlike slicerunetostring, no race because strings are immutable.    n := 0  
    for range s {  
       n++  
    }  
  
    var a []rune  
    if buf != nil && n <= len(buf) {  
       *buf = [tmpStringBufSize]rune{}  
       a = buf[:n]  
    } else {  
       a = rawruneslice(n)  
    }  
  
    n = 0  
    for _, r := range s {  
       a[n] = r  
       n++  
    }  
    return a  
}

转 []bytes

转换方向 操作 是否分配新内存 是否复制 安全性
string → []byte []byte(s) ✅ 是 ✅ 是 ✅ 安全
[]byte → string string(b) ✅ 是 ✅ 是 ✅ 安全

测试代码

go 复制代码
func string_impl() {
    s1 := "My name is 张朝阳"
    arr := []byte(s1)
    brr := []rune(s1)
    fmt.Printf("last byte %d\n", arr[len(arr)-1]) // string可以转换为[]byte或[]rune类型
    fmt.Printf("last byte %c\n", arr[len(arr)-1]) // %c以unicode字符进行输出
    fmt.Printf("last rune %d\n", brr[len(brr)-1])
    fmt.Printf("last rune %c\n", brr[len(brr)-1])
    L := len(s1)
    fmt.Printf("string len %d byte array len %d rune
    array len %d\n",L,len(arr),len(brr))
    for _,ele := range sl {
    	fmt.Printf("%c",ele)//按rune进行遍历输出
    }
    fmt.Println()
    for i := 0;i<L;i++ {
    	fmt.Printf("%c",s1[i])//[i]前面应该出现数组或切片,这里自动把string转成了[]byte(而不是[]rune)
    }
    fmt.Println()
    arr[0]=9
    // s1[0]=9·//字符串不能修改
    fmt.Println(utf8.RunecountInstring(s1),len([]rune(s1)))//查看string里有几个rune
}

和 C 语言 []char 的区别

[]char 本质是一个字节数组

特征 C语言 Go语言
内存管理 手动 自动
可变性 可变 不可变
结束符 \0结尾 存储长度
字符集 ASCII/手动UTF-8 默认UTF-8
标准库 <string.h> stringsbytesunicode/utf8
安全性 容易出错 更安全
性能 较高但需谨慎 略低但稳定
相关推荐
低调小一2 小时前
LRU缓存科普与实现(Kotlin 与 Swift)
开发语言·缓存·kotlin
Chan162 小时前
批处理优化:从稳定性、性能、数据一致性、健壮性、可观测性五大维度,优化批量操作
java·spring boot·后端·性能优化·java-ee·intellij-idea·优化
Rexi3 小时前
Go.mod版本号规则:语义化版本
后端
浅川.253 小时前
xtuoj string
开发语言·c++·算法
望获linux3 小时前
【实时Linux实战系列】实时系统的可观测性:Prometheus 与 Grafana 集成
大数据·linux·服务器·开发语言·网络·操作系统
Ray663 小时前
guide-rpc-framework vs Dubbo 实现
后端
Qperable3 小时前
gitlab-runner提示401 Unauthorized
后端·gitlab
Rexi3 小时前
Go 模块(Go.mod)核心知识与实操
后端
yinke小琪3 小时前
面试官:谈谈为什么要拆分数据库?有哪些方法?
java·后端·面试