阿里云-云小站(无限量代金券发放中)
【腾讯云】云服务器、云数据库、COS、CDN、短信等热卖云产品特惠抢购

Python 编码的前世今生

287次阅读
没有评论

共计 6262 个字符,预计需要花费 16 分钟才能阅读完成。

导读 一旦走上了编程之路,如果你不把编码问题搞清楚,那么它将像幽灵一般纠缠你整个职业生涯,各种灵异事件会接踵而来,挥之不去。只有充分发挥程序员死磕到底的精神你才有可能彻底摆脱编码问题带来的烦恼。

我第一次遇到编码问题是写 JavaWeb 相关的项目,一串字符从浏览器游离到应用程序代码中,翻江倒海沉浸到数据库中,随时随地都有可能踩到编码的地雷。第二次遇到编码问题就是学 Python 的时候,在爬取网页数据时,编码问题又出现了,当时我的心情是崩溃的,用时下最 ing 的一句话就是:“我当时就懵逼了”。

为了搞清字符编码,我们得从计算机的起源开始,计算机中的所有数据,不论是文字、图片、视频、还是音频文件,本质上最终都是按照类似 01010101 的数字形式存储的。我们是幸运的,我们也是不幸的,幸运的是时代赋予了我们都有机会接触计算机,不幸的是,计算机不是我们国人发明的,所以计算机的标准得按美帝国人的习惯来设计,那么最开始计算机是通过什么样的方式来表现字符的呢?这要从计算机编码的发展史说起。

ASCII

每个做 JavaWeb 开发的新手都会遇到乱码问题,每个做 Python 爬虫的新手都会遇到编码问题,为什么编码问题那么蛋疼呢?这个问题要从 1992 年 Guido van Rossum 创造 Python 这门语言说起,那时的 Guido 绝对没想到的是 Python 这门语言在今天会如此受大家欢迎,也不会想到计算机发展速度会如此惊人。Guido 在当初设计这门语言时是不需要关心编码的,因为在英语世界里,字符的个数非常有限,26 个字母(大小写)、10 个数字、标点符号、控制符,也就是键盘上所有的键所对应的字符加起来也不过是一百多个字符而已。这在计算机中用一个字节的存储空间来表示一个字符是绰绰有余的,因为一个字节相当于 8 个比特位,8 个比特位可以表示 256 个符号。于是聪明的美国人就制定了一套字符编码的标准叫 ASCII(American Standard Code for Information Interchange),每个字符都对应唯一的一个数字,比如字符 A 对应的二进制数值是 01000001,对应的十进制就是 65。最开始 ASCII 只定义了 128 个字符编码,包括 96 个文字和 32 个控制符号,一共 128 个字符,只需要一个字节的 7 位就能表示所有的字符,因此 ASCII 只使用了一个字节的后 7 位,最高位都为 0。
Python 编码的前世今生

EASCII(ISO/8859-1)

然而计算机慢慢地普及到其他西欧地区时,他们发现还有很多西欧所特有的字符是 ASCII 编码表中没有的,于是后来出现了可扩展的 ASCII 叫 EASCII,顾名思义,它是在 ASCII 的基础上扩展而来,把原来的 7 位扩充到 8 位,它完全兼容 ASCII,扩展出来的符号包括表格符号、计算符号、希腊字母和特殊的拉丁符号。然而 EASCII 时代是一个混乱的时代,大家没有统一标准,他们各自把最高位按照自己的标准实现了自己的一套字符编码标准,比较著名的就有 CP437,CP437 是 Windows 系统中使用的字符编码,如下图:
Python 编码的前世今生
另外一种被广泛使用的 EASCII 还有 ISO/8859-1(Latin-1),它是国际标准化组织(ISO)及国际电工委员会(IEC)联合制定的一系列 8 位元字符集的标准,ISO/8859-1 只继承了 CP437 字符编码的 128-159 之间的字符,所以它是从 160 开始定义的,不幸的是这些众多的 ASCII 扩充字集之间互不兼容。
Python 编码的前世今生

GBK

随着时代的进步,计算机开始普及到千家万户,比尔盖茨让每个人桌面都有一台电脑的梦想得以实现。但是计算机进入中国不得不面临的一个问题就是字符编码,虽然咱们国家的汉字是人类使用频率最多的文字,汉字博大精深,常见的汉字就有成千上万,这已经大大超出了 ASCII 编码所能表示的字符范围了,即使是 EASCII 也显得杯水车薪,于是聪明的中国人自己弄了一套编码叫 GB2312,又称 GB0,1981 由中国国家标准总局发布。GB2312 编码共收录了 6763 个汉字,同时它还兼容 ASCII。GB2312 的出现,基本满足了汉字的计算机处理需要,它所收录的汉字已经覆盖中国大陆 99.75% 的使用频率。不过 GB2312 还是不能 100% 满足中国汉字的需求,对一些罕见的字和繁体字 GB2312 没法处理,后来就在 GB2312 的基础上创建了一种叫 GBK 的编码。GBK 不仅收录了 27484 个汉字,同时还收录了藏文、蒙文、维吾尔文等主要的少数民族文字。同样 GBK 也是兼容 ASCII 编码的,对于英文字符用 1 个字节来表示,汉字用两个字节来标识。

Unicode

对于如何处理中国人自己的文字我们可以另立山头,按照我们自己的需求制定一套编码规范,但是计算机不止是美国人和中国人用啊,还有欧洲、亚洲其他国家的文字诸如日文、韩文全世界各地的文字加起来估计也有好几十万,这已经大大超出了 ASCII 码甚至 GBK 所能表示的范围了,况且人家为什么用采用你 GBK 标准呢?如此庞大的字符库究竟用什么方式来表示好呢?于是统一联盟国际组织提出了 Unicode 编码,Unicode 的学名是“Universal Multiple-Octet Coded Character Set”,简称为 UCS。
Python 编码的前世今生
Unicode 有两种格式:UCS-2 和 UCS-4。UCS-2 就是用两个字节编码,一共 16 个比特位,这样理论上最多可以表示 65536 个字符,不过要表示全世界所有的字符显然 65536 个数字还远远不够,因为光汉字就有近 10 万个,因此 Unicode 4.0 规范定义了一组附加的字符编码,UCS-4 就是用 4 个字节(实际上只用了 31 位,最高位必须为 0)。
Unicode 理论上完全可以涵盖一切语言所用的符号。世界上任何一个字符都可以用一个 Unicode 编码来表示,一旦字符的 Unicode 编码确定下来后,就不会再改变了。但是 Unicode 有一定的局限性,一个 Unicode 字符在网络上传输或者最终存储起来的时候,并不见得每个字符都需要两个字节,比如一字符“A“,用一个字节就可以表示的字符,偏偏还要用两个字节,显然太浪费空间了。第二问题是,一个 Unicode 字符保存到计算机里面时就是一串 01 数字,那么计算机怎么知道一个 2 字节的 Unicode 字符是表示一个 2 字节的字符呢,还是表示两个 1 字节的字符呢,如果你不事先告诉计算机,那么计算机也会懵逼了。Unicode 只是规定如何编码,并没有规定如何传输、保存这个编码。例如“汉”字的 Unicode 编码是 6C49,我可以用 4 个 ASCII 数字来传输、保存这个编码;也可以用 UTF-8 编码的 3 个连续的字节 E6 B1 89 来表示它。关键在于通信双方都要认可。因此 Unicode 编码有不同的实现方式,比如:UTF-8、UTF-16 等等。这里的 Unicode 就像英语一样,做为国与国之间交流世界通用的标准,每个国家有自己的语言,他们把标准的英文文档翻译成自己国家的文字,这是实现方式,就像 UTF-8。

UTF-8

UTF-8(Unicode Transformation Format)作为 Unicode 的一种实现方式,广泛应用于互联网,它是一种变长的字符编码,可以根据具体情况用 1-4 个字节来表示一个字符。比如英文字符这些原本就可以用 ASCII 码表示的字符用 UTF-8 表示时就只需要一个字节的空间,和 ASCII 是一样的。对于多字节(n 个字节)的字符,第一个字节的前 n 为都设为 1,第 n+1 位设为 0,后面字节的前两位都设为 10。剩下的二进制位全部用该字符的 UNICODE 码填充。
Python 编码的前世今生
以汉字“好”为例,“好”对应的 Unicode 是 597D,对应的区间是 0000 0800 — 0000 FFFF,因此它用 UTF-8 表示时需要用 3 个字节来存储,597D 用二进制表示是:0101100101111101,填充到 1110xxxx 10xxxxxx 10xxxxxx 得到 11100101 10100101 10111101,转换成 16 进制:E5A5BD,因此“好”的 Unicode“597D”对应的 UTF-8 编码是“E5A5BD”。

中文 好
unicode 0101 100101 111101
编码规则 1110xxxx 10xxxxxx 10xxxxxx
————————–
utf-8 11100101 10100101 10111101
————————–
16 进制 utf-8 e 5 a 5 b d
Python 字符编码

现在总算把理论说完了。再来说说 Python 中的编码问题。Python 的诞生时间比 Unicode 要早很多,Python 的默认编码是 ASCII。
>>> import sys
>>> sys.getdefaultencoding()
‘ascii’
所以在 Python 源代码文件中如果不显式地指定编码的话,将出现语法错误

#test.py

print “ 你好 ”
上面是 test.py 脚本,运行

python test.py

就会包如下错误:

File“test.py”, line 1 yntaxError: Non-ASCII character‘/xe4′ in file test.py on line 1, but no encoding declared;

为了在源代码中支持非 ASCII 字符,必须在源文件的第一行或者第二行显示地指定编码格式:

# coding=utf-8

或者是:

#!/usr/bin/python
# -*- coding: utf-8 -*-

在 Python 中和字符串相关的数据类型,分别是 str、unicode 两种,他们都是 basestring 的子类,可见 str 与 unicode 是两种不同类型的字符串对象。

basestring
      /  / 
     /    /
   str    unicode

对于同一个汉字“好”,用 str 表示时,它对应的就是 UTF-8 编码‘/xe5/xa5/xbd’,而用 Unicode 表示时,它对应的符号就是u’/u597d’,与 u “ 好 ” 是等同的。需要补充一点的是,str 类型的字符其具体的编码格式是 UTF-8 还是 GBK,还是其它格式,根据操作系统相关。比如在 Windows 系统中,cmd 命令行中显示的:

# windows 终端
>>> a = '好'
>>> type(a)
<type 'str'>
>>> a
'/xba/xc3'

而在 Linux 系统的命令行中显示的是:

# linux 终端
>>> a='好'
>>> type(a)
<type 'str'>
>>> a
'/xe5/xa5/xbd'

>>> b=u'好'
>>> type(b)
<type 'unicode'>
>>> b
u'/u597d'

不论是 Python3x、Java 还是其他编程语言,Unicode 编码都成为了语言的默认编码格式,而数据最后保存到介质中的时候,不同的介质可有用不同的方式,有些人喜欢用 UTF-8,有些人喜欢用 GBK,这都无所谓,只要平台统一的编码规范,具体怎么实现并不关心。

Python 编码的前世今生

str 与 unicode 的转换

那么在 Python 中 str 和 unicode 之间是如何转换的呢?这两种类型的字符串类型之间的转换就是靠这两个方法:decode 和 encode。

Python 编码的前世今生

# 从 str 类型转换到 unicode
s.decode(encoding)   =====>  <type 'str'> to <type 'unicode'>
#从 unicode 转换到 str
u.encode(encoding)   =====>  <type 'unicode'> to <type 'str'>

>>> c = b.encode('utf-8')
>>> type(c)
<type 'str'>
>>> c
'/xe5/xa5/xbd'

>>> d = c.decode('utf-8')
>>> type(d)
<type 'unicode'>
>>> d
u'/u597d'

这个 ‘/xe5/xa5/xbd’ 就是 Unicode u’ 好 ’ 通过函数 encode 编码得到的 UTF-8 编码的 str 类型的字符串。反之亦然,str 类型的 c 通过函数 decode 解码成 Unicode 字符串 d。

str(s) 与 unicode(s)

str(s) 和 unicode(s) 是两个工厂方法,分别返回 str 字符串对象和 Unicode 字符串对象,str(s) 是 s.encode(‘ascii’) 的简写。实验:

>>> s3 = u"你好"
>>> s3
u'/u4f60/u597d'
>>> str(s3)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
UnicodeEncodeError: 'ascii' codec can't encode characters in position 0-1: ordinal not in range(128)

上面 s3 是 Unicode 类型的字符串,str(s3)相当于是执行 s3.encode(‘ascii’),因为“你好”两个汉字不能用 ASCII 码来表示,所以就报错了,指定正确的编码:s3.encode(‘gbk’) 或者 s3.encode(‘utf-8’) 就不会出现这个问题了。类似的 Unicode 有同样的错误:

>>> s4 = "你好"
>>> unicode(s4)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
UnicodeDecodeError: 'ascii' codec can't decode byte 0xc4 in position 0: ordinal not in range(128)
>>>

unicode(s4)等效于s4.decode(‘ascii’)
,因此要正确的转换就要正确指定其编码 s4.decode(‘gbk’) 或者 s4.decode(‘utf-8’)。

乱码

所有出现乱码的原因都可以归结为字符经过不同编码解码在编码的过程中使用的编码格式不一致,比如:

# encoding: utf-8

>>> a='好'
>>> a
'/xe5/xa5/xbd'
>>> b=a.decode("utf-8")
>>> b
u'/u597d'
>>> c=b.encode("gbk")
>>> c
'/xba/xc3'
>>> print c

UTF-8 编码的字符‘’占用 3 个字节,解码成 Unicode 后,如果再用 GBK 来解码后,只有 2 个字节的长度了,最后出现了乱码的问题,因此防止乱码的最好方式就是始终坚持使用同一种编码格式对字符进行编码和解码操作。

Python 编码的前世今生

其他技巧

对于如 Unicode 形式的字符串(str 类型):

s = 'id/u003d215903184/u0026index/u003d0/u0026st/u003d52/u0026sid'

转换成真正的 Unicode 需要使用:

s.decode('unicode-escape')

测试:

>>> s = 'id/u003d215903184/u0026index/u003d0/u0026st/u003d52/u0026sid/u003d95000/u0026i'
>>> print(type(s))
<type 'str'>
>>> s = s.decode('unicode-escape')
>>> s
u'id=215903184&index=0&st=52&sid=95000&i'
>>> print(type(s))
<type 'unicode'>
>>>

以上代码和概念都是基于 Python2.x。

阿里云 2 核 2G 服务器 3M 带宽 61 元 1 年,有高配

腾讯云新客低至 82 元 / 年,老客户 99 元 / 年

代金券:在阿里云专用满减优惠券

正文完
星哥玩云-微信公众号
post-qrcode
 0
星锅
版权声明:本站原创文章,由 星锅 于2024-07-24发表,共计6262字。
转载说明:除特殊说明外本站文章皆由CC-4.0协议发布,转载请注明出处。
【腾讯云】推广者专属福利,新客户无门槛领取总价值高达2860元代金券,每种代金券限量500张,先到先得。
阿里云-最新活动爆款每日限量供应
评论(没有评论)
验证码
【腾讯云】云服务器、云数据库、COS、CDN、短信等云产品特惠热卖中

星哥玩云

星哥玩云
星哥玩云
分享互联网知识
用户数
4
文章数
19352
评论数
4
阅读量
8018495
文章搜索
热门文章
星哥带你玩飞牛NAS-6:抖音视频同步工具,视频下载自动下载保存

星哥带你玩飞牛NAS-6:抖音视频同步工具,视频下载自动下载保存

星哥带你玩飞牛 NAS-6:抖音视频同步工具,视频下载自动下载保存 前言 各位玩 NAS 的朋友好,我是星哥!...
星哥带你玩飞牛NAS-3:安装飞牛NAS后的很有必要的操作

星哥带你玩飞牛NAS-3:安装飞牛NAS后的很有必要的操作

星哥带你玩飞牛 NAS-3:安装飞牛 NAS 后的很有必要的操作 前言 如果你已经有了飞牛 NAS 系统,之前...
我把用了20年的360安全卫士卸载了

我把用了20年的360安全卫士卸载了

我把用了 20 年的 360 安全卫士卸载了 是的,正如标题你看到的。 原因 偷摸安装自家的软件 莫名其妙安装...
再见zabbix!轻量级自建服务器监控神器在Linux 的完整部署指南

再见zabbix!轻量级自建服务器监控神器在Linux 的完整部署指南

再见 zabbix!轻量级自建服务器监控神器在 Linux 的完整部署指南 在日常运维中,服务器监控是绕不开的...
飞牛NAS中安装Navidrome音乐文件中文标签乱码问题解决、安装FntermX终端

飞牛NAS中安装Navidrome音乐文件中文标签乱码问题解决、安装FntermX终端

飞牛 NAS 中安装 Navidrome 音乐文件中文标签乱码问题解决、安装 FntermX 终端 问题背景 ...
阿里云CDN
阿里云CDN-提高用户访问的响应速度和成功率
随机文章
欧洲无限速云盘免费10GB永久存储 + WebDAV部署+图床搭建,多平台联动一步到位!

欧洲无限速云盘免费10GB永久存储 + WebDAV部署+图床搭建,多平台联动一步到位!

欧洲无限速云盘免费 10GB 永久存储 + WebDAV 部署 + 图床搭建,多平台联动一步到位! 大家好,我...
国产开源公众号AI知识库 Agent:突破未认证号限制,一键搞定自动回复,重构运营效率

国产开源公众号AI知识库 Agent:突破未认证号限制,一键搞定自动回复,重构运营效率

国产开源公众号 AI 知识库 Agent:突破未认证号限制,一键搞定自动回复,重构运营效率 大家好,我是星哥,...
恶意团伙利用 PHP-FPM 未授权访问漏洞发起大规模攻击

恶意团伙利用 PHP-FPM 未授权访问漏洞发起大规模攻击

恶意团伙利用 PHP-FPM 未授权访问漏洞发起大规模攻击 PHP-FPM(FastCGl Process M...
星哥带你玩飞牛 NAS-9:全能网盘搜索工具 13 种云盘一键搞定!

星哥带你玩飞牛 NAS-9:全能网盘搜索工具 13 种云盘一键搞定!

星哥带你玩飞牛 NAS-9:全能网盘搜索工具 13 种云盘一键搞定! 前言 作为 NAS 玩家,你是否总被这些...
240 元左右!五盘位 NAS主机,7 代U硬解4K稳如狗,拓展性碾压同价位

240 元左右!五盘位 NAS主机,7 代U硬解4K稳如狗,拓展性碾压同价位

  240 元左右!五盘位 NAS 主机,7 代 U 硬解 4K 稳如狗,拓展性碾压同价位 在 NA...

免费图片视频管理工具让灵感库告别混乱

一言一句话
-「
手气不错
零成本上线!用 Hugging Face免费服务器+Docker 快速部署HertzBeat 监控平台

零成本上线!用 Hugging Face免费服务器+Docker 快速部署HertzBeat 监控平台

零成本上线!用 Hugging Face 免费服务器 +Docker 快速部署 HertzBeat 监控平台 ...
还在找免费服务器?无广告免费主机,新手也能轻松上手!

还在找免费服务器?无广告免费主机,新手也能轻松上手!

还在找免费服务器?无广告免费主机,新手也能轻松上手! 前言 对于个人开发者、建站新手或是想搭建测试站点的从业者...
欧洲无限速云盘免费10GB永久存储 + WebDAV部署+图床搭建,多平台联动一步到位!

欧洲无限速云盘免费10GB永久存储 + WebDAV部署+图床搭建,多平台联动一步到位!

欧洲无限速云盘免费 10GB 永久存储 + WebDAV 部署 + 图床搭建,多平台联动一步到位! 大家好,我...
每年0.99刀,拿下你的第一个顶级域名,详细注册使用

每年0.99刀,拿下你的第一个顶级域名,详细注册使用

每年 0.99 刀,拿下你的第一个顶级域名,详细注册使用 前言 作为长期折腾云服务、域名建站的老玩家,星哥一直...
星哥带你玩飞牛NAS硬件02:某鱼6张左右就可拿下5盘位的飞牛圣体NAS

星哥带你玩飞牛NAS硬件02:某鱼6张左右就可拿下5盘位的飞牛圣体NAS

星哥带你玩飞牛 NAS 硬件 02:某鱼 6 张左右就可拿下 5 盘位的飞牛圣体 NAS 前言 大家好,我是星...