go字符串
Last updated
Was this helpful?
Last updated
Was this helpful?
go操作字符串,会遇到一个很反直觉的事情:看起来是字母,但拿出来的却是数字。
比如下面这段代码:
elem 49 int32 i 49 uint8 elem 97 int32 i 97 uint8 elem 24069 int32 i 229 uint8 len(str) 5
你看,用for/range拿出来的字符串,每个elem
都是一串数字,类型是int32
。
但是你改用str[i]
拿出来,类型就变成了uint8
数字和英文拿出来的结果都是一样的,1
对应49
,a
对应97
但中文的结果却不一样,帅
用elem
拿对应的是24069
,用str[i]
拿对应的却是229
而且看起来这个str
只有三位,打印出来的长度却是5。
很反直觉不是么?把我看得满脸问号🤪
从中可以知道,gloang中的string,底层是通过byte数组实现的。换言之,string
就是[]byte
。
byte是啥呢?byte,就是这里的uint8
。就是用str[i]
取出来的数据类型
比如这里1
对应49
(00110001),a
对应97
(01100001),用的就是ASCII码的翻译规则。
那么问题来了,存字符串用byte
,为什么用elem
取出来的数据类型是int32
呢?为什么取出来的时候还要特地转一下数据类型呢?
这个问题和中文的两个取值结果不同有关。
英语用128个符号编码就够了,但其他语言就不够。尤其汉字有10w多,只用一个字节最多表示256种符号,因此要用多个字节来表达一个中文字。
于是产生了unicode的编码方法,相当于一个大字典,全世界的符号都有独一无二的编码了。
比如帅
的unicode是5e05
,对应二进制(1011110 00000101),对应10进制就是我们输出的24069。
那么问题来了,帅
的unicode明显只有两个byte啊,5e
和05
。英文和数字各1个字节,那么总长度应该是4,为什么我们输出的长度是5呢?
unicode只是用来找二进制对应的字符,用于翻译的。但用这个来存储和传输,并不方便。
既然中文需要两个byte,那么怎么区分英文和中文呢?因此大家都要存两个字节。这样英文和数字就很不划算。
比如用unicode存,就需要6个byte
字节1
字节2
十进制
1
00000000
00110001
49
a
00000000
01100001
97
帅
01011110
00000101
94 05
因此就出现了一个方便unicode存储和传输的方法:utf-8
用utf-8存,只要5个字节
字节1
字节2
字节3
十进制
1
00110001
49
a
01100001
97
帅
11100101
10111000
10000101
229 184 133
这就是为什么我们用s[i]
取出来的是229,因为一共有5位,我们只取了第3位,而第三位就是229.
这就是为什么我们字符串的长度是5,因为最底层是用utf-8存的。
go把存着的utf-8编码的一个个byte取出来,翻译成unicode,根据这个unicode找到对应的字符,最终显示出来给我们看。
换言之,我们看到的字母、数字、汉字都是幻觉,本质上我们操纵的都是这一位位的rune,或者说是int32
这也就解释了为什么elem
取出来的时候,需要转一下数据类型。因为我们操作的是这个rune,这个unicode翻译出来的字符。不转一个elem只能取出1/3的“帅”了😂😂(其实会输出乱码)
s[i]
和len()
是一起的,操作的是最底层的一个个byte。
答案是把字符串(本质为[]byte
类型),转成[]rune
类型
rune [49 97 24069] 3 byte [49 97 229 184 133] 5
当然也可以用utf8这个包:
从中可以知道,计算机最底层,是用1和0来存东西的。但0和1就两个二进制位(bit),也就只能代表两个东西,肯定不够。
于是就用8个0或者1,组成一个字节(byte),用一个byte来代表字母或数字。这个组成的规则叫。
utf-8里面,汉字是三个字节,因为它,让计算机能知道,这个汉字从哪开始到哪结束。
那么问题来了,我们该?