一、引入问题-为何打印s[0] 没有打印‘你’字符
package main
import "fmt"
func main() {
s := "你"
fmt.Println(s[0])
fmt.Printf("%s\n", s[0])
}
- output
%!s(uint8=228)
首先需要知道go中编码格式和String 类型, Go内置的utf-8编码格式。
二、utf-8编码 与 Unicode
- Unicode,全称为Unicode标准(The Unicode Standard),其官方机构Unicode联盟所用的中文名称为统一码,为各种语言中的每个字符定义一个唯一的编码,也描述为码点。
- utf-8编码 是一种可变长编码方式,可以用一至四个字节对有效码点进行编码。
‘a' 对应的Unicode值为U+0061 -- utf-8编码后自己 “\x61”(前128个字符与ASCII编码兼容)
‘你’ 对应的Unicode值为U+4F60 -- utf-8编码后字节 “\xe4\xbd\xa0”
- 详细介绍 utf-8编码介绍 wiki Unicode介绍
三、String类型
- 在GO中String类型可以存储任意字节,在编写代码过程,当定义好一个字符串时,存储的是utf-8编码后的字节, 也就是说以下的s和s1字符串是等价的
package main
import "fmt"
func main() {
s := "你"
s1 := "\xe4\xbd\xa0"
if s == s1 {
fmt.Println("s == s1")
}
}
output
s == s1
可以看到 s字符串实际存储的是对‘你’字符utf-8编码后的字节,那么回到最开始的问题,当使用下标进行访问的时候,s[0]打印的应该是编码后的第一个字节
fmt.Printf("%x", s[0])
output
e4 //确实如猜测一样打印第一个字节
那么是如何解决这个问题,按字符来进行访问,而不是字节?
四、rune类型
4.1定义
在builtin/builtin.go文件中定义描述
// 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
主要作用是用来区分字符值和整数值
4.2 应用
4.2.1 将字符串转化为rune类型
package main
import "fmt"
func main() {
s := "你好"
s1 := []rune(s)
fmt.Printf("字节个数: %d\n", len(s))
fmt.Printf("字符个数: %d\n", len(s1))
}
output
字节个数: 6
字符个数: 2
4.2.2 range
因为在GO中utf-8是唯一的编码方式,因此在对字符串使用range遍历时,会对字符串使用utf-8进行解码,转化为rune类型输出
package main
import "fmt"
func main() {
s := "你好"
for _, c := range s {
fmt.Printf("%s\n", string(c))
}
}
output
你
好
五、参考资料
Strings, bytes, runes and characters in Go
utf-8编码介绍 wiki
Unicode介绍