一文详解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 只读 |
是否引用同一底层数据 | ❌ 否 | ❌ 否 | 完全新分配 |
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) | ✅ 是 | ✅ 是 | ✅ 安全 |
测试代码
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 runearray 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> | strings 、bytes 、unicode/utf8 |
安全性 | 容易出错 | 更安全 |
性能 | 较高但需谨慎 | 略低但稳定 |