文章目录
- 总览
- 编码概念
- Ascii 字符集
- Unicode 字符集
- UTF-8 编码方式
- 读写
- Python2的编码
- encode 和 decode
- 解释器 和 cmd
- Tips
总览
编码是绕不开的问题, 归根结底, 编码就是 数据从 机器的格式(二进制) 到 人类可读格式(符号)
同时还有大端, 小端问题
以下全部使用大端表示: 地址由小向大增加, 数据从高位往低位放. 类似于字符串, 左边是低地址, 放高位
编码概念
首先最起码的认知:
计算机只有0, 1, 因此要表达一个人类可读的符号, 必须要有某种规范, 抽象出来, 就是所谓的字符集.
计算机的底层表示:
0110 0001 0110 0001
这是人类无法阅读的, 所以必须按照某种规则, 将其转为人类可理解的.
那么问题来了, 怎么读, 一次读几位, 读完又按照什么去翻译? 这也就是规则, 也就是编码要解决的问题.
正确的读出, 再去字符集表里去查找即可.
Ascii 字符集
- 怎么读: 一次读1个byte, 也就是8bit. 因此 上面的表达 对应两个符号
0110 0001
,0110 0001
- 怎么解释: 查表呗, 查表上面就是
01100001
对应 ‘a’
所以, 其实上面 "翻译"出来, 就是’aa’
Unicode 字符集
Ascii因为位数的限制, 因此可表示的符号有限, 为了兼容全世界所有的语言, 因此, Unicode出现了, 并且兼容Ascii
计算机底层表示:
0000 0000 0110 0001 0000 0000 0110 0001
- 怎么读: 一次读2个byte, 也就是16bit. 因此 上面的表达 对应两个符号
0000 0000 0110 0001
,0000 0000 0110 0001
- 怎么解释: 查表, 查表上面就是
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’ |
|
|
|
‘你’ | 无能为力 |
|
|
读写
现在假设想将 ‘你’ 写入到某个文件, 然后把该文件发给某个终端, 那本质就是将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
- unicode 是 一种字符集, 也就是一个 字符 到 其二进制 的映射, 而utf-8, 是一个算法. 不是同一个概念!
- 看看Python的Struct库