这个问题看似简单,但是要总结一下,写代码的时候思路可以更清晰,顺便比较一下string, rune和byte[]几个数据类型的区别。

操作系统中的字符编码

ASCII码

上个世纪60年代,美国制定了一套字符编码,对英文字符与二进制位之间的关系,做了统一的规定,并称为ASCII码,一直沿用至今。ASCII码一共规定了128个字符编码,比如空格“SPACE”是32(二进制00100000),大写字母A是65(二进制01000001)。 这128个符号(包括32个不能打印出来的控制符号), 只占用了一个字节的后面7位,最前面的1位统一规定为0。简单一点来说,就是使用单子节的整数表示英文字符,我们称之为字符的ASCII码表示。以后有人问我们,“A”的ASCII码是什么?直接回答65就可以了。

Unicode字符集

在英文中,仅仅用128个符号就足够了,但是在其他文字中是远远不够的。这时候,Unicode就出现了。Unicode s将全世界的字符都纳入其中,每一个字符都用独一无二的编码。比如,U+0639表示阿拉伯字母Ain,U+0041表示英语的大写字母A,U+4E25表示汉字”严”。

这时候,我们来思考一个问题?表示中文的”严”至少需要两个字节,但是英文字符字符只需要一个字节。如果英文字符的前面字节全部变为0,就会造成极大的浪费。所以造成了Unicode字符集在世界上有多种存储方式,而且迟迟无法推广,直到互联网的兴起才解决了这个问题。

UTF-8

utf-8编码的出现,几乎统一整个互联网,形成了事实上标准。简单来说,UTF-8就是在互联网上使用最广的一种Unicode的实现方式。

String

在go语言中,string就是读取使用utf-8编码的字节切片(slice)。因此使用len函数得到的长度并不是字符个数,而是字节个数。for循环遍历也是输出各个字节。

  1. a := "Randal";
  2. for i := 0; i < len(a); i++ {
  3. fmt.Printf("%x ", a[i])
  4. fmt.Printf("%c ", a[i])
  5. }
  6. // 输出结果
  7. 52 61 6e 64 61 6c
  8. Randal
  9. a := "中国";
  10. fmt.Println(len(a))
  11. for i := 0; i < len(a); i++ {
  12. fmt.Printf("%x ", a[i])
  13. }
  14. for i := 0; i < len(a); i++ {
  15. fmt.Printf("%c ", a[i])
  16. }
  17. // 输出结果
  18. 6
  19. E4 B8 AD E5 9B BD
  20. 中å½

但我们在对中文使用格式化输出的时候,出现了乱码。原因是当字符的utf-8编码超过1个字节的时候,格式化输出单个字符就会出现乱码的情况,rune可以帮助我们解决乱码的问题。

rune

rune是int32的别名, 代表字符的unicode码,采用4个字节存储。将string转成rune就意味着任何一个字符都是使用4个字节才存储unicode码。这样每次遍历的时候返回的就是unicode,而不再是字节,便可以解决乱码的问题。

  1. var s string
  2. s = "中国"
  3. r := []rune(s)
  4. for i := 0; i < len(r); i++ {
  5. fmt.Printf("%x", r[i])
  6. }
  7. for i := 0; i < len(r); i++ {
  8. fmt.Printf("%c", r[i])
  9. }
  10. // 输出结果
  11. 4e2d 56fd
  12. 中国

通过for range对字符串进行遍历时,每次获取到的对象都是rune类型的,因此下面的方式也可以解决乱码问题。

  1. var s string
  2. s = "中国"
  3. for _, item := range s {
  4. fmt.Printf("%c", item)
  5. }
  6. // 输出结果
  7. 中国

bytes

byte操作的对象也是字节切片,与string的不可变不同,byte是可变的。go的字符串是utf-8编码的,每个字符长度是不确定的。

  1. s1 := "abcd"
  2. b1 := []byte(s1)
  3. fmt.Println(b1) // [97 98 99 100]
  4. s2 := "中文"
  5. b2 := []byte(s2)
  6. fmt.Println(b2) // [228 184 173 230 150 135], unicode,每个中文字符会由三个byte组成
  7. // byte和string的转化
  8. var data [10]byte
  9. byte[0] = 'T'
  10. byte[1] = 'E'
  11. var str string = string(data[:])