• 欢迎访问搞代码网站,推荐使用最新版火狐浏览器和Chrome浏览器访问本网站!
  • 如果您觉得本站非常有看点,那么赶紧使用Ctrl+D 收藏搞代码吧

关于python:深入理解Python字符编码

python 搞代码 4年前 (2022-02-20) 40次浏览 已收录 0个评论

不管你是有着多年教训的 Python 老司机还是刚入门 Python 不久,你肯定遇到过UnicodeEncodeError、UnicodeDecodeError 谬误,每当遇到谬误咱们就拿着 encode、decode 函数翻来覆去的转换,有时试着试着问题就解决了,有时候怎么试都没辙,只有借用 Google 大神帮忙,但仿佛很少去关怀问题的实质是什么,下次遇到相似的问题吃一堑;长一智,那么你有没有想过一次性彻底把 Python 字符编码给搞懂呢?

齐全了解字符编码 与 Python 的渊源前,咱们有必要把一些根底概念弄清楚,尽管有些概念咱们每天都在接触甚至在应用它,但并不一定真正了解它。比方:字节、字符、字符集、字符码、字符编码。

字节

字节(Byte)是计算机中数据存储的根本单元,一字节等于一个8位的比特,计算机中的所有数据,不论是保留在磁盘文件上的还是网络上传输的数据(文字、图片、视频、音频文件)都是由字节组成的。

字符

你正在浏览的这篇文章就是由很多个字符(Character)形成的,字符一个信息单位,它是各种文字和符号的统称,比方一个英文字母是一个字符,一个汉字是一个字符,一个标点符号也是一个字符。

字符集

字符集(Character Set)就是某个范畴内字符的汇合,不同的字符集规定了字符的个数,比方 ASCII 字符集总共有128个字符,蕴含了英文字母、阿拉伯数字、标点符号和控制符。而 GB2312 字符集定义了7445个字符,蕴含了绝大部分汉字字符。

字符码

字符码(Code Point)指的是字符集中每个字符的数字编号,例如 ASCII 字符集用 0-127 间断的128个数字别离示意128个字符,例如 “A” 的字符码编号就是65。

字符编码

字符编码(Character Encoding)是将字符集中的字符码映射为字节流的一种具体实现计划,常见的字符编码有 ASCII 编码、UTF-8 编码、GBK 编码等。某种意义上来说,字符集与字符编码有种对应关系,例如 ASCII 字符集对应 有 ASCII 编码。ASCII 字符编码规定应用单字节中低位的7个比特去编码所有的字符。例如”A” 的编号是65,用单字节示意就是0×41,因而写入存储设备的时候就是b’01000001’。

编码、解码

编码的过程是将字符转换成字节流,解码的过程是将字节流解析为字符。


了解了这些根本的术语概念后,咱们就能够开始探讨计算机的字符编码的演进过程了。

从 ASCII 码说起

说到字符编码,要从计算机的诞生开始讲起,计算机创造于美国,在英语世界里,罕用字符十分无限,26个字母(大小写)、10个数字、标点符号、控制符,这些字符在计算机中用一个字节的存储空间来示意入不敷出,因为一个字节相当于8个比特位,8个比特位能够示意256个符号。于是美国国家标准协会ANSI制订了一套字符编码的规范叫 ASCII(American Standard Code for Information Interchange),每个字符都对应惟一的一个数字,比方字符 “A” 对应数字是65,”B” 对应 66,以此类推。最早 ASCII 只定义了128个字符编码,包含96个文字和32个管制符号,一共128个字符只须要一个字节的7位就能示意所有的字符,因而 ASCII 只应用了一个字节的后7位,剩下最高位1比特被用作一些通信零碎的奇偶校验。下图就是 ASCII 码字符编码的十进制、二进制和字符的对应关系表

扩大的 ASCII:EASCII(ISO/8859-1)

然而计算机缓缓地遍及到其余西欧地区时,发现还有很多西欧字符是 ASCII 字符集中没有的,显然 ASCII 曾经没法满足人们的需要了,好在 ASCII 字符只用了字节的7位 0×00~0x7F 共128个字符,于是他们在 ASCII 的根底上把原来的7位裁减到8位,把0×80-0xFF这前面的128个数字利用起来,叫 EASCII ,它齐全兼容ASCII,扩大进去的符号包含表格符号、计算符号、希腊字母和非凡的拉丁符号。然而 EASCII 时代是一个凌乱的时代,各个厂家都有本人的想法,大家没有统一标准,他们各自把最高位依照本人的规范实现了本人的一套字符编码标准,比拟驰名的就有 CP437, CP437 是 始祖IBM PC、MS-DOS应用的字符编码,如下图:

泛滥的 ASCII 裁减字符集之间互不兼容,这样导致人们无奈失常交换,例如200在CP437字符集示意的字符是 È ,在 ISO/8859-1 字符集外面显示的就是 ╚,于是国际标准化组织(ISO)及国际电工委员会(IEC)联结制订的一系列8位字符集的规范ISO/8859-1(Latin-1),它继承了 CP437 字符编码的128-159之间的字符,所以它是从160开始定义的,ISO-8859-1在 CP437 的根底上从新定义了 160~255之间的字符。

多字节字符编码 GBK

ASCII 字符编码是单字节编码,计算机进入中国后面临的一个问题是如何解决汉字,对于拉丁语系国家来说通过扩大最高位,单字节示意所有的字符曾经入不敷出,然而对于亚洲国家来说一个字节就显得顾此失彼了。于是中国人本人弄了一套叫 GB2312 的双字节字符编码,又称GB0,1981 由中国国家标准总局公布。GB2312 编码共收录了6763个汉字,同时他还兼容 ASCII,GB 2312的呈现,根本满足了汉字的计算机解决须要,它所收录的汉字曾经笼罩中国大陆99.75%的应用频率,不过 GB2312 还是不能100%满足中国汉字的需要,对一些常见的字和繁体字 GB2312 没法解决,起初就在GB2312的根底上创立了一种叫 GBK 的编码,GBK 不仅收录了27484个汉字,同时还收录了藏文、蒙文、维吾尔文等次要的少数民族文字。同样 GBK 也是兼容 ASCII 编码的,对于英文字符用1个字节来示意,汉字用两个字节来标识。

Unicode 的问世

GBK仅仅只是解决了咱们本人的问题,然而计算机不止是美国人和中国人用啊,还有欧洲、亚洲其余国家的文字诸如日文、韩文全世界各地的文字加起来预计也有好几十万,这曾经大大超出了ASCII 码甚至GBK 所能示意的范畴了,尽管各个国家能够制订本人的编码方案,然而数据在不同国家传输就会呈现各种各样的乱码问题。如果只用一种字符编码就能示意地球甚至火星上任何一个字符时,问题就迎刃而解了。是它,是它,就是它,咱们的小英雄,对立联盟国内组织提出了Unicode 编码,Unicode 的学名是”Universal Multiple-Octet Coded Character Set”,简称为UCS。它为世界上每一种语言的每一个字符定义了一个惟一的字符码,Unicode 规范应用十六进制数字示意,数字后面加上前缀 U+,比方字母『A』的Unicode编码是 U+0041,汉字『中』的Unicode 编码是U+4E2D

Unicode有两种格局:UCS-2和UCS-4。UCS-2就是用两个字节编码,一共16个比特位,这样实践上最多能够示意65536个字符,不过要示意全世界所有的字符显示65536个数字还远远不过,因为光汉字就有近10万个,因而Unicode4.0标准定义了一组附加的字符编码,UCS-4就是用4个字节(实际上只用了31位,最高位必须为0)。实践上齐全能够涵盖所有语言所用的符号。

Unicode 的局限

然而 Unicode 有肯定的局限性,一个 Unicode 字符在网络上传输或者最终存储起来的时候,并不见得每个字符都须要两个字节,比方字符“A“,用一个字节就能够示意的字符,偏偏还要用两个字节,显然太节约空间了。

第二问题是,一个 Unicode 字符保留到计算机外面时就是一串01数字,那么计算机怎么晓得一个2字节的Unicode字符是示意一个2字节的字符呢,例如“汉”字的 Unicode 编码是 U+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码填充。

以『好』为例,『好』对应的 Unicode 是597D,对应的区间是 0000 0800–0000 FFFF,因而它用 UTF-8 示意时须要用3个字节来存储,597D用二进制示意是: 0101100101111101,填充到 1110xxxx 10xxxxxx 10xxxxxx 失去 11100101 10100101 10111101,转换成16进制是 e5a5bd,因而『好』的 Unicode 码 U+597D 对应的 UTF-8 编码是 “E5A5BD”。你能够用 Python 代码来验证:

>>> a = u”好”

>>> a

u’u597d’

>>> b = a.encode(‘utf-8’)

>>> len(b)

3

>>> b

‘xe5xa5xbd’


当初总算把实践说完了。再来说说 Python 中的编码问题。Python 的诞生工夫比 Unicode 要早很多,Python2 的默认编码是ASCII,正因为如此,才导致很多的编码问题。

>>> import sys

>>> sys.getdefaultencoding()

‘ascii’

所以在 Python2 中,源代码文件必须显示地指定编码类型,否则凡是代码中呈现有中文就会报语法错误

# coding=utf-8

或者是:

# –– coding: utf-8 –

Python2 字符类型

在 python2 中和字符串相干的数据类型有 str 和 unicode 两种类型,它们继承自 basestring,而 str 类型的字符串的编码格局能够是 ascii、utf-8、gbk等任何一种类型。

对于汉字『好』,用 str 示意时,它对应的 utf-8 编码 是’xe5xa5xbd’,对应的 gbk 编码是 ‘xbaxc3’,而用 unicode 示意时,他对应的符号就是u’u597d’,与u”好” 是等同的。

str 与 unicode 的转换

在 Python 中 str 和 unicode 之间是如何转换的呢?这两种类型的字符串之间的转换就是靠decode 和 encode 这两个函数。encode 负责将unicode 编码成指定的字符编码,用于存储到磁盘或传输到网络中。而 decode 办法是依据指定的编码方式解码后在应用程序中应用。

 _#__从unicode转换到str用 encode_

>>> b = u’好’

>>> c = b.encode(‘utf-8’)

>>> type(c)

`<type` 'str'`>`

>>> c

‘xe5xa5xbd’

#__从str类型转换到unicode用decode

>>> d = c.decode(‘utf-8’)

>>> type(d)

`<type` 'unicode'`>`

>>> d

u’u597d’

UnicodeXXXError 谬误的起因

在字符编码转换操作时,遇到最多的问题就是 UnicodeEncodeError 和 UnicodeDecodeError 谬误了,这些谬误的根本原因在于 Python2 默认是应用 ascii 编码进行 decode 和 encode 操作,例如:

case 1

>>> s = ‘你好’

>>> s.decode()

Traceback (most recent call last):
 `File` "<stdin>"`, line` 1`,` **in** `<module>`
`UnicodeDecodeError:` 'ascii' `codec can`'t decode byte 0xe4 in position 0: ordinal not in range(128)

当把 s 转换成 unicode 类型的字符串时,decode 办法默认应用 ascii 编码进行解码,而 ascii 字符集中基本就没有中文字符『你好』,所以就呈现了 UnicodeDecodeError,正确的形式是显示地指定 UTF-8 字符编码。

>>> s.decode(‘utf-8’)

u’u4f60u597d’

同样地情理,对于 encode 操作,把 unicode字符串转换成 str类型的字符串时,默认也是应用 ascii 编码进行编码转换的,而 ascii 字符集找不到中文字符『你好』,于是就呈现了UnicodeEncodeError 谬误。

>>> a = u’你好’

>>> a.encode()

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)

case 2

str 类型与 unicode 类型的字符串混合应用时,str 类型的字符串会隐式地将 str 转换成 unicode字符串,如果 str字符串是中文字符,那么就会呈现UnicodeDecodeError 谬误,因为 python2 默认会应用 ascii 编码来进行 decode 操作。

>>> s = ‘你好’ # str__类型

>>> y = u’python’ # unicode__类型

>>> s + y # _隐式转换,即 s.decode(‘ascii’) + u_

Traceback (most recent call last):
 `File` "<stdin>"`, line` 1`,` **in** `<module>`
`UnicodeDecodeError:` 'ascii' `codec can`'t decode byte 0xe4 in position 0: ordinal not in range(128)

正确地形式是显示地指定 UTF-8 字符编码进行解码

>>> s.decode('utf-8') +y
`u'u4f60u597dpython'`

乱码

所有呈现乱码的起因都能够归结为字符通过不同编码解码在编码的过程中应用的编码格局不统一,比方:

# encoding: utf-8

>>> a=‘好’

>>> a

‘xe5xa5xbd’

>>> b=a.decode(“utf-8”)

>>> b

u’u597d’

>>> c=b.encode(“gbk”)

>>> c

‘xbaxc3’

>>> print c

utf-8编码的字符‘好’占用3个字节,解码成Unicode后,如果再用gbk来解码后,只有2个字节的长度了,最初呈现了乱码的问题,因而避免乱码的最好形式就是始终保持应用同一种编码格局对字符进行编码和解码操作。


搞代码网(gaodaima.com)提供的所有资源部分来自互联网,如果有侵犯您的版权或其他权益,请说明详细缘由并提供版权或权益证明然后发送到邮箱[email protected],我们会在看到邮件的第一时间内为您处理,或直接联系QQ:872152909。本网站采用BY-NC-SA协议进行授权
转载请注明原文链接:关于python:深入理解Python字符编码

喜欢 (0)
[搞代码]
分享 (0)
发表我的评论
取消评论

表情 贴图 加粗 删除线 居中 斜体 签到

Hi,您需要填写昵称和邮箱!

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址