前言

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

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

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

是什么

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

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

// runtime/string.go
type 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)
}
} /* output
hi,狗 a[0] is 104
a[0:2] is hi b is hi,狗狗 a's length is: 6 0 104
1 105
2 44
3 231
4 139
5 151 0 104
1 105
2 44
3 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/utf8" 标准库,手动实现 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
}
} /* output
U+0068 'h' starts at byte position 0
U+0069 'i' starts at byte position 1
U+002C ',' starts at byte position 2
U+72D7 '狗' starts at byte position 3
*/

参考

Strings, bytes, runes and characters in Go

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

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

最新文章

  1. SpringMVC操作指南-MVC-搭建SpringMVC项目结构(基于Java API和注解)
  2. 不完全解决Android微信HTML5 播放视频的问题(不显示控制条,可交互)
  3. Ptex源码学习笔记-2
  4. ROS 多台电脑间进行通信
  5. jq实现点击弹出框代码
  6. java学习第十一天
  7. psycopg2.pool – Connections pooling / psycopg2.pool – 连接池 / postgresql 连接池
  8. Python Thread
  9. Tomcat安全
  10. Web软件开发工具WebBuilder试用手记
  11. 驱动力—— 通信引擎(上)—— ESFramework 4.0 进阶(03)
  12. MySQL DNS反查导致连接缓慢
  13. Spring quartz 单机、集群+websocket集群实现文本、图片、声音、文件下载及推送、接收及显示
  14. 为你详解Linux安装GCC方法
  15. 【Java】 剑指offer(32) 从上往下打印二叉树
  16. [转载]Java中的String,StringBuilder,StringBuffer三者的区别
  17. 安装docker跨主机网络flannel
  18. vue-cli2 构建速度优化
  19. Quartz框架多个trigger任务执行出现漏执行的问题分析--转
  20. 树莓派进阶之路 (028) - 树莓派SQLite3的安装

热门文章

  1. 剑指Offer 55. 数组中的逆序对
  2. 1- java语言特性
  3. 06- web兼容性测试与web兼容性测试工具
  4. LA3135简单多路归并(优先队列)
  5. 8.PHP图像处理
  6. C#-WiFi共享
  7. Root mapping definition has unsupported parameters
  8. python-内置函数-compile,eval,exec
  9. Unreal: Dynamic load map from Pak file
  10. 常用加密算法学习总结之数字证书与TLS/SSL