golang – 关于字符编码
不同语言在编码处理的部分各有不同,但是套路基本就2种。
以C/C++为一派,它们因为是编译成机器码的语言,所以在编码处理方面比较原始:
源代码文件是采用什么编码写的,那么字符串就是什么编码,完全可以用GBK编码开发程序。
Java,Python则是另外一派:
源代码文件必须是UTF-8的,解释器会在运行时把源代码中的UTF-8字符转成Unicode存储
Golang呢,其实属于中间状态:
它要求源代码必须是UTF-8,但是编译二进制时仍旧会保持字符串的UTF-8编码。
string一定是utf-8编码
我们在IDE中编写的Go源代码都是UTF-8的,因此下面的string是UTF-8的:
1 2 3 |
s := "你" // utf-8编码 fmt.Println(len(s)) // 字节长度 fmt.Println(s[1]) // 按字节访问 |
求string的长度实际是字节长度而不是字符个数,因此:
- len(s)是3
- s[1]可以取到第1个字节
遍历字符必须用rune
因为UTF-8是变长编码,为了按数组风格访问到每一个字符,就需要先转回Unicode。
在Golang中,Unicode字符的数据类型是rune,它实际是int32类型的别名,为什么unicode字符需要4个字节呢?因为Unicode最新标准是 UCS-4,也就是用4个字节可以表示世界上任意的字符,所以Golang把存储空间是一步做到位了。
1 2 3 |
r := []rune(s) // 转成unicode fmt.Println(len(r)) // unicode字符个数 fmt.Println(r[0]) // 打印1个unicode字符 |
在golang中并不需要特殊的编码转换函数,从UTF-8转Unicode是Golang语言级支持的,只需要对string或者[]byte做[]rune转换即可解码为Unicode字符数组。
因此:
- len(r)输出1
- r[0]输出20320,也就是”你”的Unicode十进制表示,如果要打印”你”,需要用string(r[0])完成从Unicode到Utf-8的反向编码,这是Golang语言级自动支持的。
range遍历string的特殊性
利用for+range遍历string,得到的并不是字节,而是Unicode字符,也就是逐个的rune。
从UTF-8编码到Unicode的解码过程也是Golang语言级自动支持的:
1 2 3 |
for _, c := range s { fmt.Println(c) } |
上述代码将输出20230,如果要打印成可见字符串,还是应该把Unicode编码回Utf-8:
1 2 3 |
for _, c := range s { fmt.Println(string(c)) } |
如果你就是想逐个字节的遍历string,那么先把string转成[]byte即可。
向其他编码转换
如果我们想得到GBK编码的”你”,在Golang世界中并不是以Unicode作为基准的,而是以UTF-8编码,这与其他语言的差别是比较大的。
我们需要安装一个Golang标准扩展库:
go get golang.org/x/text/…
其中…的意思是递归引入golang.org/x/text项目下面所有子项目(目录),所谓项目就是有go.mod的目录。
然后,我们只需要这样一个操作,就可以完成从UTF-8编码 -> Unicode编码 -> GBK编码的全过程:
1 2 3 |
reader := transform.NewReader(strings.NewReader(s), simplifiedchinese.GBK.NewEncoder()) g,_ := ioutil.ReadAll(reader) fmt.Println(g) |
我们只需要传入UTF-8字符串,transform配合GBK的encoder可以完成剩余2步转换。
如果文章帮助您解决了工作难题,您可以帮我点击屏幕上的任意广告,或者赞助少量费用来支持我的持续创作,谢谢~
