AI智能
改变未来

Go string 一清二楚

前言

字符串(string) 作为 go 语言的基本数据类型,在开发中必不可少,我们务必深入学习一下,做到一清二楚。

本文假设读者已经知道切片(slice)的使用,如不了解,可阅读 Go 切片 基本知识点

为了更好的理解后文,推荐先阅读 Unicode 字符集,UTF-8 编码

是什么

In Go, a string is in effect a read-only slice of bytes.

在 go 语言中,字符串实际上是一个只读字节切片,其数据结构定义如下:

// runtime/string.gotype stringStruct struct {str unsafe.Pointer	// 指向底层字节数组的指针len int				// 字节数组的长度}

注意:byte 其实是 uint8 的类型别名

// byte is an alias for uint8 and is equivalent to uint8 in all ways. It is// used, by convention, to distinguish byte values from 8-bit unsigned// integer values.type byte = uint8

怎么用

func main() {// 使用字符串字面量初始化var a = \"hi,狗\"fmt.Println(a)// 可以使用下标访问,但不可修改fmt.Printf(\"a[0] is %d\\n\", a[0])fmt.Printf(\"a[0:2] is %s\\n\", a[0:2])// a[0] = \'a\' 编译报错,Cannot assign to a[0]// 字符串拼接var b = a + \"狗\"fmt.Printf(\"b is %s\\n\", b)// 使用内置 len() 函数获取其长度fmt.Printf(\"a\'s length is: %d\\n\", len(a))// 使用 for;len 遍历for i := 0; i < len(a); i++ {fmt.Println(i, a[i])}// 使用 for;range 遍历for i, v := range a {fmt.Println(i, v)}}/* outputhi,狗a[0] is 104a[0:2] is hib is hi,狗狗a\'s length is: 60 1041 1052 443 2314 1395 1510 1041 1052 443 29399*/

如果读者在看上面的代码时有疑惑,不用着急,下文将会挨个解读。

只读

字符串常量会在编译期分配到只读段,对应数据地址不可写入,相同的字符串常量不会重复存储

func main() {var a = \"hello\"fmt.Println(a, &a, (*reflect.StringHeader)(unsafe.Pointer(&a)))a = \"world\"fmt.Println(a, &a, (*reflect.StringHeader)(unsafe.Pointer(&a)))var b = \"hello\"fmt.Println(b, &b, (*reflect.StringHeader)(unsafe.Pointer(&b)))}/* output字符串字面量 该变量的内存地址 底层字节切片hello 0xc0000381f0 &{5033779 5}world 0xc0000381f0 &{5033844 5}hello 0xc000038220 &{5033779 5}*/

可以看到 hello 在底层只存储了一份

for;len 遍历

go 的源代码都是 UTF-8 编码格式的,上例中的”狗“字占用三个字节,即 231 139 151(Unicode Character Table),所以上例的运行结果很清楚。

于此同时,也可以将字符串转化为字节切片

func main() {var a = \"hi,狗\"b := []byte(a)fmt.Println(b)	// [104 105 44 231 139 151]}

for;range 遍历

The Unicode standard uses the term \”code point\” to refer to the item represented by a single value.

在 Unicode 标准中,使用术语

code point

来表示由单个值表示的项,通俗点来说,U+72D7(十进制表示为 29399)代表符号 ”狗“

\”Code point\” is a bit of a mouthful, so Go introduces a shorter term for the concept: rune.

code point 有点拗口,所以在 go 语言中专门有一个术语来代表它,即 rune

注意:rune 其实是 int32 的类型别名

// rune is an alias for int32 and is equivalent to int32 in all ways. It is// used, by convention, to distinguish character values from integer values.type rune = int32

在对字符串类型进行 for;range 遍历时,其实是按照 rune 类型来解码的,所以上例的运行结果也很清晰。

与此同时,也可以将字符串转化为 rune 切片

func main() {// 使用字符串字面量初始化var a = \"hi,狗\"r := []rune(a)fmt.Println(r) // [104 105 44 29399]}

当然我们也可以使用

\"unicode/ut1044f8\"

标准库,手动实现 for;range 语法糖相同的效果

func main() {var a = \"hi,狗\"for i, w := 0, 0; i < len(a); i += w {runeValue, width := utf8.DecodeRuneInString(a[i:])fmt.Printf(\"%#U starts at byte position %d\\n\", runeValue, i)w = width}}/* outputU+0068 \'h\' starts at byte position 0U+0069 \'i\' starts at byte position 1U+002C \',\' starts at byte position 2U+72D7 \'狗\' starts at byte position 3*/

参考

Strings, bytes, runes and characters in Go

为什么说go语言中的string是不可变的?

字符咋存?utf8咋编码?string啥结构?

赞(0) 打赏
未经允许不得转载:爱站程序员基地 » Go string 一清二楚