文章目录

  • 总览
  • 编码概念
  • Ascii 字符集
  • Unicode 字符集
  • UTF-8 编码方式
  • 读写
  • Python2的编码
  • encode 和 decode
  • 解释器 和 cmd
  • Tips


总览

编码是绕不开的问题, 归根结底, 编码就是 数据从 机器的格式(二进制) 到 人类可读格式(符号)

同时还有大端, 小端问题

以下全部使用大端表示: 地址由小向大增加, 数据从高位往低位放. 类似于字符串, 左边是低地址, 放高位

编码概念

首先最起码的认知:

计算机只有0, 1, 因此要表达一个人类可读的符号, 必须要有某种规范, 抽象出来, 就是所谓的字符集.

计算机的底层表示:

0110 0001 0110 0001

这是人类无法阅读的, 所以必须按照某种规则, 将其转为人类可理解的.

那么问题来了, 怎么读, 一次读几位, 读完又按照什么去翻译? 这也就是规则, 也就是编码要解决的问题.

正确的读出, 再去字符集表里去查找即可.

Ascii 字符集

  1. 怎么读: 一次读1个byte, 也就是8bit. 因此 上面的表达 对应两个符号 0110 0001, 0110 0001
  2. 怎么解释: 查表呗, 查表上面就是 01100001 对应 ‘a’

所以, 其实上面 "翻译"出来, 就是’aa’

Unicode 字符集

Ascii因为位数的限制, 因此可表示的符号有限, 为了兼容全世界所有的语言, 因此, Unicode出现了, 并且兼容Ascii

计算机底层表示:

0000 0000 0110 0001 0000 0000 0110 0001

  1. 怎么读: 一次读2个byte, 也就是16bit. 因此 上面的表达 对应两个符号 0000 0000 0110 0001, 0000 0000 0110 0001
  2. 怎么解释: 查表, 查表上面就是0000 0000 0110 0001 对应 ‘a’

所以, 其实上面 "翻译"出来, 依旧是’aa’

那么问题来了, Ascii规则下的’aa’, 只需要2个byte, 但是Unicode下, 就需要4个byte了, 相当于增加了一倍, 这对于计算内存是很不友好的.

那么随之想的是, 发现’a’对应的unicode编码, 前面全是0, 那是不是可以省略?

答案: 可以, 但是省略后, 读取的规则就也要随之改变, 以前固定读2byte, 但是现在来说, 有时候是2 byte, 有时候又是1 byte.

所以UTF应运而生, UTF全称其实就是 Unicode Transform Format. 由此抽象出概念: 编码/解码方式, 在Unicode字符集之上, 二次进行编码

UTF-8 编码方式

UTF存在多种方式, UTF-8是目前使用最多的.

UTF-8是一种变长的编码方式, Unicode固定2 byte, utf-8则为 1 - 6个 byte, 但是目前一般最多用到3个byte

符号

ascii

unicode(16进制)

utf-8(16进制)

‘a’

0110 0001(61)

0000 0000 0110 0001 (00 61)

0110 0001 (61)

‘你’

无能为力

0100 1111 0110 0000 (4F 60)

1110 0100 1011 1101 1010 0000 (E4 BD A0)

读写

现在假设想将 ‘你’ 写入到某个文件, 然后把该文件发给某个终端, 那本质就是将byte写入文件, 然后通过网络发送该字节流.

首先的问题: "你"这个字符串, 在应用程序里是如何表达? 这取决于编程语言. 假设我们的编程语言, 内存里表达"你"是 4F 60(Unicode),

但是网络一般传输的形式是’utf-8’, 因此在应用程序写入文件的时候, 我们需要将Unicode通过utf-8的编码方式进行编码, 那么写入文件, 结果就是 E4 BD A0,

所以当对端收到后, 想要正确解读出信息, 就是将E4 BD A0按照utf-8解码方式进行解码, 将解出的unicode字节流放入自己的程序运行空间中, 当然放入后, 该终端的程序再怎么去存储, 就不是要关注的了

Python2的编码

Python的字符串分为两种格式: str 和 Unicode

Unicode好理解, 底层的byte就是Unicode的排布方式.

但是str是什么? 底层是的byte是怎么排布?

坑点就在这里: str的具体形式, 取决于这个文件的编码形式.

也就是说, 如果在编写这个代码文件的时候, 保存的时候是gbk编码, 那么所有str的byte其实就是gbk的排布方式

那么如果将某些数据写入或者发送, 那么gbk要先转unicode, 再转utf-8.

所以一般python2的开头都是要声明编码方式: coding=utf-8, 这样, 其实所有str的byte就全是按照utf-8排布的

encode 和 decode

把unicode的byte表达形式, 理解成一个最初始, 最本质的形态, 那么encode和decode, 就是让unicode的byte表示 和 另一种二次编码的byte表示 相互转换

编码方式就可以理解为 “算法”, 以下假设编码方式是utf-8:

这就很好理解了, encode, 编码, 就是正向算法, unicode -> utf-8的表示

decode, 解码, 就是反向算法, utf-8的表示 -> unicode

那么注意这个箭头, 这个箭头的过程, 是一个算法. 可以理解为类似密钥.

utf8_s = "你"  # 默认是utf-8的编码
unicode_s = s.decode('utf-8')  # unicode编码
gbk_s = unicode_s.encode('gbk')  # gbk编码

gbk_s.decode('utf-8')  # 报错, 因为"密钥"不对了, gbk的byte用utf-8解释, 显然不行

解释器 和 cmd

s = "你"
s

如果是在linux的环境下, 开启python解释器, 输入上面的代码, 会显示’\xe4 \xbd \xa0’, 也就是utf-8的编码方式

如果是在windows的环境下, 开启python解释器, 输入上面的代码, 会显示’\xc4 \xe3’

因为linux环境下, 解释器的默认编码方式就是utf-8, 而windows下是gbk.

另外, print是无法看出是str还是unicode的, 因为字节流会被解码, 具体看看print对编码的处理就ok了, 所以print有时候会出乱码, 而控制台直接查看变量就不会

Tips

  1. unicode 是 一种字符集, 也就是一个 字符 到 其二进制 的映射, 而utf-8, 是一个算法. 不是同一个概念!
  2. 看看Python的Struct库