文章目录
- Go语言字符串的本质
- 和其他类型的转换(内存结构的变化)
- [和 C 语言 `[]char` 的区别](#和 C 语言
[]char
的区别)
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> |
strings 、bytes 、unicode/utf8 |
安全性 | 容易出错 | 更安全 |
性能 | 较高但需谨慎 | 略低但稳定 |