如题,在解释三者差别之前,我们先看一个实例:
package mainimport ("fmt")func main() {c := "是谁在看我呢QAQ"fmt.Println(c, fmt.Sprintf("len: %d", len(c)))fmt.Println([]byte(c), fmt.Sprintf("len: %d", len([]byte(c))))fmt.Println([]rune(c), fmt.Sprintf("len: %d", len([]rune(c))))}---是谁在看我呢QAQ len: 21[230 152 175 232 176 129 229 156 168 231 156 139 230 136 145 229 145 162 81 65 81] len: 21[26159 35841 22312 30475 25105 21602 81 65 81] len: 9
我们知道,utf-8
编码中,一个中文字符是占三个字节, 一个英文字符占一个字节。
从表象看起来,string
和byte
类型中的len
为字节码长度,而rune
的len
才计算的是字符串个数。
golang
中的string
底层应该是用byte
数组存储的,而且属于不可变类型。
计算机是二进制的,字符最终也是转换成二进制保存起来的。
字符集就是定义字符对应的数值。Unicode
是一个字符集,为每个字符规定表达的数字,但是并没有规定该数字的二进制保存方式,utf8
作为可变长度字符编码,规定了对于unicode
值的二进制保存方式。
比如,a
字符的unicode
值为97
,占用1个字节,而中
字符的unicode
值为20013
,则占用3个字节。
string
string
类型表示 8 位字节字符串值的集合。字符串的长度表示字节数,永远不会为负数。字符串是不可变的, 一旦创建,就不可能更改;可以为空,但不能是
nil
;可以通过
0
~len(s)-1
来访问字符串字节元素, 如s[i]
;获取字符串元素的地址是非法的, 如&s[i];
Byte
type byte = uint8 byte 是 uint8 的别名, 在所有方面都等价于 uint8
byte
采用1
个字节存储,将string
转成byte
就意味着用1个字节来存储其unicode
值。
Rune
type byte = uint32 // rune 是 int32 的别名,在所有方面都等价于 uint32
rune
采用4
个字节存储,将string
转成rune
就意味着任何一个字符都用4个字节来存储其unicode
值。
看完上面的定义,我们来看另外一个例子:
s := "是谁在看我呢QAQ"fmt.Println(s[:2]) // 可以截取,但是会乱码显示??
直接截取取得是string
底层数组得字节码,两个字节不能表示中文字符,自然会乱码。
有人说我直接取三个可行?
似乎取了底层三个字节显示一个中文字符,那取前6个字节切片应该可以行:
fmt.Println(s[:6]) // 是谁
可如果字符串不包含中文字符或者第二个字符直接是英文字符,你就懵逼了:
s2 := "123qab"fmt.Println(s2[:6]) // "123qab"
正确的做法是:
fmt.Println([]rune(s)[:2]) // [26159 35841], 类型是[]rune,可以看成[]uint32fmt.Println(string([]rune(s)[:2])) // 是谁,类型是string
本篇就介绍这么多,感谢大佬三连支持~
关注公众号"玩转云原生",不定期搞点不一样的黑科技。




