字符编码

* 本页面主要介绍Go语言字符编码的相关内容。

学过计算机原理的同学都知道,计算机只能处理数字,因为它只认识二进制0和1,但是我们却可以在计算机上存储和使用各种各样的文字和符号,这就要归功于字符编码!

一、基础概念

开始详细了解字符编码之前,我们明确几个定义:

  • 字符集(Charset):是一个系统支持的所有抽象字符的集合。字符是各种文字和符号的总称,包括各国家文字、标点符号、图形符号、数字等。常见字符集名称:ASCII字符集、GB2312字符集、BIG5字符集、GB18030字符集、Unicode字符集等。
  • 字符编码(Character Encoding):是一套法则,使用该法则能够对自然语言的字符的一个集合(如字母表或音节表),与其他东西的一个集合(如号码或电脉冲)进行配对。即在符号集合与数字系统之间建立对应关系,它是信息处理的一项基本技术。通常人们用符号集合(一般情况下就是文字)来表达信息。而以计算机为基础的信息处理系统则是利用元件(硬件)不同状态的组合来存储和处理信息的。元件不同状态的组合能代表数字系统的数字,因此字符编码就是将符号转换为计算机可以接受的数字系统的数,称为数字代码。

例如,在ASCII编码中,我们用编码数字65表示 大写字母 A。由于计算机是美国人发明的,他们为了可以处理字符,就把大小写字母、数字和一些符号集合成一个字符集,然后给为这些字符分配一个编码,也就是建立一个从编码到字符的映射关系表。这其实就是ASCII编码。所以说ASCII编码包含了ASCII字符集和其字符编码方式:

ASCII编码表
ASCII编码表
二、发展中不断完善

在上面的ASCII编码中,完美解决了美国人的字符编码,但是对中国人来讲就不友好了。相应的,为了解决中文在计算机中的存储和展示,我们制定了GB2312编码。但是,对于其他国家,这又不友好了。而且,在含有多种语言混合的文本中,错误的字符编码方式,有可能就会出现“乱码”的情况。所以,随着发展越来越快,影响范围越来越广,是时候有一套“标准”来统一解决字符问题了。

这就是Unicode:Unicode把所有语言的字符都统一到一套编码里。编码通常是两个字符,比如“中国的” 中,其Unicode编码\u4e2d(\u表示unicode编码),换成对应的二进制就是01001110 00101101,十进制是20013。

这下好了,全天下的文字字符都可以用一套编码来解决了。但是天下没有绝对完美的方案,虽然统一了编码,但是却“浪费了空间”。比如同样的英文文档,如果用Unicode编码的话,会比ASCII编码多出一倍的存储空间

为了解决这个问题(节约存储和提高传输效率),UTF-8编码出现了!

作者题外话:这个时代就是这样,一代又一代的新技术,推动着人类和技术的进步!不断出现的工业革命,不也是在解决问题的过程中,加速了人类的进步。从蒸汽机的发明、到电力的发明、再到电脑和互联网技术的发明,前三次工业革命,不断促进人类的进步和经济的繁荣。第四次工业革命,将以区块链技术的发明和大规模应用为标志,继续推动人类技术和经济的向前发展!

相对Unicode比较死板的编码方式,UTF-8是一种变长编码方式,它使用1~4个字节来表示一个符号,根据不同的符号而变化字节长度。UTF-8的编码规则很简单,只有两条:

  • 对于单字节的符号,字节的第一位设为0,后面7位为这个符号的 Unicode 码。因此对于英语字母,UTF-8 编码和 ASCII 码是相同的。
  • 对于n字节的符号(n > 1),第一个字节的前n位都设为1,第n + 1位设为0,后面字节的前两位一律设为10。剩下的没有提及的二进制位,全部为这个符号的 Unicode 码。
Unicode和UTF-8的对应关系
Unicode和UTF-8的对应关系

根据上表:解读 UTF-8 编码非常简单。如果一个字节的第一位是0,则这个字节单独就是一个字符M/;如果第一位是1,则连续有多少个1,就表示当前字符占用多少个字节

字符编码的发展过程是不是已经清晰了?那我们就来看看Go语言中,跟字符编码关系紧密的String、byte和Rune吧!

三、Go语言中的字符编码

String、byte和Rune其实都是“别名”而已,用来告知编译器如何解读这些二进制串

  • String :A string is an immutable sequence of bytes
  • byte:是uint8的别名
  • rune:是unit32的别名,官方定义“assigns each unicode characters a standard number which called a Unicode code point or,in Go terminology, a rune”,所以,rune就是每个Unicode字符的编码值

上代码,更直观:

 package main
  import (
    "fmt"
  )
   
  func main() {
    s := "中"
    bytes := []byte(s)
    runes := []rune(s)
    fmt.Printf("%x\n", s)  //输出:e4b8ad
    fmt.Println(len(s))    //输出: 3
    fmt.Printf("%x\n", bytes) //输出: e4b8ad
    fmt.Println(len(bytes)) //输出: 3
    fmt.Printf("%x\n", runes) //输出:4e2d
    fmt.Println(len(runes)) //输出:1
  }

很容易的就可以看出:go里面string的字符编码是UTF-8,string的底层其实就是一个byte数组,而内置函数len作用在string上时,其实就是返回这个string的byte数组的长度了,而非其rune表示。可以说

  • byte 的操作单位是一个字节,可以理解为一个英文字符
  • rune 的操作单位是一个字符,不管这个字符是什么字符

字符相关的内容就总结这么多,你学废了吗?


* 本页内容参考以下数据源:

  • 《Go程序设计语言》
  • https://home.unicode.org/
  • https://blog.csdn.net/sysushui/article/details/103229457

凯冰科技 · 代码改变世界,技术改变生活
下一篇:JSON →