Python字符编码

1. 字符编码背景


我们知道,计算机处理器只能处理二进制的数据,但人类却很难直接用二进制数据与计算机进行交流,因为哪怕只是简单的非0和1的数字,哪怕你是个程序员,也很难马上把它的二进制数据和实际数字关联起来的,何况计算机处理的可不仅仅是数字,还有标点、字母甚至各种文字。

因此,为了实现人类世界的数字、标点、字母(下面我们统称为字符)与计算机世界的二进制数据之间进行互相转换,就产生了字符编码。由于计算机是美国人发明的,且在计算机发明的初期只在美国使用,因此,最初的字符编码也是基于美国标准的。又因为美国人使用的是英语,英语只有26个字母,按一个字母算一个字符且区分大小写的话,总共才52个字符,再加上10个阿拉伯数字(0~9)和各种标点、符号等也没有超过128个字符。

因此,美国国家标准协会(ANSI)规定:使用一个7位的二进制数来编码一个字符,最多可以编码128个字符,这就是ASCII编码。如果你有学过计算机相关课程,那么下面这个ASCII编码图你应该已经很熟悉了:

在这里插入图片描述

从上图可以看出,ASCII编码用二进制的0000000-1111111总共128个数字(十进制的0~127)来编码128个字符。例如:00110000(48)代表字符“0”,01000001(65)代表字符“A”等等。

有了ASCII编码表,人类交给计算机处理的英文文本就能按照它来逐个字符地编码为计算机认识的二进制数据了;反之,计算机也可以把处理后的二进制数据保存为人类可以直接阅读的字符文本,这过程又称为字符解码。

现在许多文本编辑器都支持查看文件的十六进制模式,例如UltraEdit。我们用UltraEdit新建一个文本文档(假设默认文本编码格式是ANSI),输入任意字符,然后打开十六进制模式,可以看到类似以下内容:

在这里插入图片描述

右边“I love python”是我们输入的字符串,左边的一排数字就是每个字符对应的字符编码,这里的数字是十六进制的,即49代表0x49,也就是十进制的73,查ASCII码表得知,它就是大写字母I;同理,20就是十进制的32,在ASCII表中代表的是空格符,依次类推。

随着计算机的发展和普及,越来越多的人开始使用计算机,而不同国家和地区使用的语言和字符集也各不相同,比如中文有大几万个汉字字符,128个ASCII编码显然是捉襟见肘的。于是,为了满足不同地区的需求,字符编码开始向全球化和多语言化的方向发展,进而衍生出各种各样的字符编码格式,例如针对中文的GBK编码、BIG5编码等。此外,考虑到多国语言的混合文本,又必须要考虑统一编码的问题,于是出现了所谓的“万国码”,这些常用的编码我们将在 6.2 常用字符编码格式 中给大家展开讲讲。

字符编码的建立使得计算机可以方便地存储、传输和处理文本信息,也促进了不同地区和不同语言之间的信息交流,因此字符编码是计算机处理文本信息的基础,学习编程必须对字符编码有一定的了解,在后面的应用中才能做到庖丁解牛、游刃有余。

在这里插入图片描述

2. 常用字符编码格式


目前世界上有许多字符编码格式,我们没有能力去全部科普一遍,只能在这里简单地介绍几个碰到用到的,例如:ASCII、ANSI、GBK、unicode,其他编码都大同小异,用到时再去熟悉一下就好了。

  • ASCII编码

前面已经简单说过,ASCII用7bit的二进制编码来表示一个字符。但是一般计算机都是按字节为最小单位来处理数据的,因此每个ASCII符号编码是占满1个字节(8bit)的,只是最高位默认都是置0。

后来计算机传到了欧洲,欧洲人也有自己的字符集,此时ASCII码就不够用了,于是就使用了完整的8bit,在保留ASCII原来0~127字符集不变的情况下,又扩展出从128到255这128个新字符编码,称为扩展ASCII编码或者EASCII(Extended ASCII)码,由于EASCII码主要是针对部分西欧字符,且目前大多数软件系统采用unicode,所以EASCII应用并不广泛,我们几乎不会使用它。

  • ANSI编码

ANSI顾名思义,它是美国国家标准协会的标准码。为了对ASCII编码进行扩展,ANSI使用0x000x7F范围的1个字节来表示1个英文字符(兼容ASCII码),超出此范围的字符则使用0x800xFFFF来编码,从而使计算机支持更多的语言。

ANSI本意是一种单字节编码,即,每个字符只占用1个字节。但对于微软操作系统来说,ANSI并不止是一种编码,而是一堆编码的合称,为什么这么说呢?因为不同的国家和地区制定了不同的ANSI标准,但是都只是针对自己国家的语言。例如,在简体中文Windwos系统中,ANSI编码代表GB2312编码,在繁体中文Windows系统中,ANSI编码代表BIG5编码,在日文Windows系统中,ANSI编码代表Shift-JIS编码等。而中日韩的ANSI标准都是0x7F之后均采用2字节编码,例如汉语的“中”字的ANSI编码就是 0xd6 0xd0 两个字节,这和前面说的ANSI单字节编码一说有点矛盾!所以建议你格局打开,不要深究ANSI这一概念的准确定义,你只需要知道它在不同的操作系统中代表不同的编码标准就行了,或者简单的理解为“本地字符集”即可。

由于不同的ANSI编码之间互不兼容,所以当信息在国际间交流时,无法将属于两种语言的文字存储在同一个ANSI编码的文本中。

  • GBK编码

GBK全称GuoBiaoKuozhan,即国标扩展,对谁扩展?对GB2312扩展,GB2312是最早一版的中文字符编码,由于要和ASCII兼容,所以它是一种变长编码,即英文字符用1个字节表示,中文字符用2个字节表示,而且为了避免与ASCII字符编码冲突,GB2312中的每个汉字都必须由2个大于127(最高bit为1)的字节组合而成,GB2312中收录了6763个汉字以及682个特殊符号,已经囊括了我们日常生活中能碰见的几乎所有汉字和字符了。

GB2312采用了一种叫二维矩阵编码法来对所有字符进行编码,具体细节我就不在这里说了,感兴趣的可以去网上搜索相关资料继续深入学习。

GB2312编码全表

http://tools.jb51.net/table/gb2312

对于GB2312编码表有个值得注意的地方,那就是表中也有一些数字和字母,与ASCII编码表中的数字和字母出现了重合,例如图中的字符“2”:

在这里插入图片描述

它的GB2312编码为A3B2,而在ASCII编码表中值为50,这就是输入法中所说的“全角”和“半角”的区别:全角数字占2个字节,半角数字占1个字节。对于这些重合的字符,我们在代码中必须使用半角输入法,即使用ASCII字符而非中文字符,否则数编译器很可能会报错。


回到GBK,由于GB2312只支持6736个汉字,而前面我们有提到,中国的汉字有大几万个,GB2312显然是不能满足所有汉字编码的,于是就出现了GBK。GBK在保证兼容GB2312的前提下,也采用每个字符占2个字节的方式来继续扩展其他汉字的编码,最终达到20902个汉字+984个汉字标点符号、部首等,其中还包含了繁体字,但是要注意:GBK里面的繁体字编码与台湾省的BIG5繁体字编码是不兼容的。

GBK编码全表

http://tools.jb51.net/table/gbk_table

后来,GBK编码的两万多个汉字也逐渐满足不了我们的使用需求了,于是又发展出一个叫GB18030的编码。


我们先分析一下,用2个字节来编码一个字符,最多只有2^16=65536个编码,还要去掉避免与ASCII冲突而限制的部分编码,只剩下3万多个可用编码了。显然,继续用2个字节来编码一个字符已经没多少扩展空间了。


GB18030采用了4字节编码的方式,当然,为了兼容GBK,GB18030编码的前后2个字节都不能与GBK编码冲突,这个和前面提到的GB2312兼容ASCII时前后字节的最高bit只能为1的原理是一样的。

我国在2000年和2005年前后颁布了两次GB18030的编码标准,直至目前,GB18030收纳了7万多个汉字编码,甚至已经包含了少数民族的文字了。GBK应该是我们当前接触的最多最频繁的编码格式之一了。

  • unicode编码

前面讲过,随着计算机的发展和推广,世界各地各自为政,搞出了很多兼容ASCII但相互之间又互不兼容的编码方案,微软统一称之为ANSI编码。这样一来,对采用不同ANSI编码方案的系统之间的数据交换和信息交流就会遇到很大的障碍,甚至同样是使用中文的台湾和大陆,都有两套不一样的编码方案。最要命的是,如果一个文本中同时存在两种或两种以上的ANSI编码方案的字符时,就会出现乱码,需要使用专门的ANSI编码处理软件才能正常阅读。

为什么早期的电子邮件常常出现乱码?往往就是因为收件人和发件人使用的操作系统的ANSI编码方案不一致导致的。

于是,一种统一的编码方案呼之欲出,它踏着七彩祥云而来,是我们心目中的盖世英雄,它就是unicode,又称为统一码、万国码、单一码,它是由Adobe、苹果、IBM、微软等组成的 统一码联盟(The Unicode Consortium)制定的,于1991年发布1.0版,截止2023年5月,已经发展到15.0.0版。有趣的是,这期间,国际化标准组织(ISO)也在制定“通用字符集”(Universal Character Set简称UCS),并颁布了ISO 10646标准。这两个组织互相掐架后面又互相兼容,然后就有了今天我们看到的遵循ISO 10646 标准的unicode编码。双方在互不冲突的情况下各自发布了自己的标准,不过目前看起来unicode比较能打,应用也更为广泛。

unicode是大一统的字符集,它收集了世界上所有的字符,并进行分类,然后为每一个字符分配一个序号,这个序号的取值范围是:0x0 ~ 0x10FFFF(ISO 10646也遵循此范围约束),它不仅覆盖了当前世界上所有的字符,也为将来扩展和开发者私用留下了足够的空间。

unicode一开始规定使用2个字节来统一编码所有的字符,也就是我们今天说的utf-16编码(注意:utf-16是比utf-8更早出现的)。utf-16编码中,无论是半角的英文字母还是全角的汉字,他们都统一用2个字节来表示1个字符(对应ISO的ucs-2),也就是说连ASCII字符也被扩展到了2个字节,且ASCII字符的高9位永远是0。后来,为了支持ISO-10646的32位编码(即ucs-4),又提出了utf-32编码,它是ucs-4的子集,用4个字节来扩展字符编码。

你也许已经看出来了,utf-16和utf-32有个最大的缺点,那就是浪费空间,因为他们是固定长度编码,对于英文字符来说,按理只需要一个字节就能表示,但他们为了统一编码却要浪费1个或者3个字节来存储一个英文字符。

于是,就又出现了一种可变长度的unicode编码格式,那就是万众瞩目的utf-8编码了,utf-8编码保留了ASCII编码的兼容性,即只要是以0开头的字符都表示它是一个ASCII字符,大大的节省了存储的空间和数据处理的时间。注意的是utf-8是用3个字节来表示一个汉字,和GBK不大一样哦。

你可以像前面举例一样,用UltraEdit来查看各种编码格式下的中英文符号的编码值,来验证我们上面介绍的知识,多动手才能加深理解。

目前世界上绝大多数操作系统都支持unicode编码,特别是utf-8编码,它已经成为了电子邮件、网页及其他存储或者传送文件的默认编码格式。我们前面课程也说过,Python代码也应该默认以utf-8格式来保存,其他大部分编程语言的代码也一样。

3. Python字符编码处理


写了大量的背景知识作为铺垫,终于回到Python编程中来了,我们前面大量的科普并不是不务正业,所谓知其然不如知其所以然,希望读者能体谅我们的良苦用心。

如果你学过Python 2,你应该知道有一种叫unicode的数据类型,但是Python 3中没有unicode类型,而是合并到str类型中,字符串默认是以unicode编码的,因此Python直接支持多国语言,例如:

print("I love python")
print("我爱python")

输出是完全正常的:

I love python
我爱python

█ 可以使用ord()函数用来获取字符编码。

例如获取“中”字的unicode编码:

code = ord("中")
print(code)

输出结果是20013,即十六进制的0x4E2D。

█ 可以使用chr()函数来进行字符解码。

例如我们可以通过unicode编码来获取字符:

char = chr(22269)  # 或者写十六机制0x56FD
print(char)

输出“国”字。

█ Python允许直接用\u转义符来表示unicode字符编码。

注意\u后面必须是十六进制的数字,如:

print('\u4E2D\u56FD')

输出“中国”。


前面讲Python数据类型时,没有给大家提到一种叫bytes的数据类型,特意放到这里来讲。Python中bytes类型用于表示二进制数据,它与字符串类型的主要区别在于,bytes类型中元素是字节,而字符串类型中元素是字符,在处理网络数据或者二进制文件时,常常会用到bytes类型,例如,当你打开一个二进制文件时,文件的内容将以bytes对象的形式读入。


Python字符串在内存中是以unicode形态存放的,我们可以把它转换为bytes类型进行存储或者网络传输,对于ASCII字符,我们可以用b前缀加单引号或双引号的方式来进行转换,例如:

dat = b'python'     # 用双引号也可以
print(type(dat))

输出如下:

所以,'python’和b’python’是两个完全不一样的东西哦。

非ASCII字符不能用这种方式转换,但可以用encode()和decode()方法来转换,当然这两个接口也适用于ASCII字符,如:

name = "python"
print(name.encode())

name = "中国"
print(name.encode())

code = b"python"
print(code.decode())

输出如下:

b'python'
b'\xe4\xb8\xad\xe5\x9b\xbd
python

可以看到,“中国”的bytes看起来像一个utf-8的字符编码(3个字节表示一个汉字),那是因为encode() 和 decode() 函数的默认编码格式就是utf-8,当然,你也可以指定其他编码格式,如:

name = "中国"
print(name.encode('gb2312'))

输出如下:

b'\xd6\xd0\xb9\xfa'
从上面例子可以看出,对于ASCII字符,其bytes数据是直接显示b’xx’形式的,而非ASCII字符,则显示为b’\\x##’(\\x##为十六进制数)。

前面课程已经讲过,可以用len()方法来计算字符串长度,得到的是字符串中字符的个数。bytes类型也可以用len()方法来计算数据长度,计算结果是字节数,而不是字符数,例如:

name = "中国"
data = name.encode('gb2312')

print(len(name))
print(len(data))

运行结果如下:

2
4

回过头去看 Python文件结构 你应该明白为什么我们要求Python代码要保存为utf-8格式,并在文件头部加上如下声明了。

# -*- coding:utf-8 -*-

题外话

当下这个大数据时代不掌握一门编程语言怎么跟的上脚本呢?当下最火的编程语言Python前景一片光明!如果你也想跟上时代提升自己那么请看一下.

在这里插入图片描述

感兴趣的小伙伴,赠送全套Python学习资料,包含面试题、简历资料等具体看下方。

一、Python所有方向的学习路线

Python所有方向的技术点做的整理,形成各个领域的知识点汇总,它的用处就在于,你可以按照下面的知识点去找对应的学习资源,保证自己学得较为全面。

img
img

二、Python必备开发工具

工具都帮大家整理好了,安装就可直接上手!img

三、最新Python学习笔记

当我学到一定基础,有自己的理解能力的时候,会去阅读一些前辈整理的书籍或者手写的笔记资料,这些笔记详细记载了他们对一些技术点的理解,这些理解是比较独到,可以学到不一样的思路。

img

四、Python视频合集

观看全面零基础学习视频,看视频学习是最快捷也是最有效果的方式,跟着视频中老师的思路,从基础到深入,还是很容易入门的。

img

五、实战案例

纸上得来终觉浅,要学会跟着视频一起敲,要动手实操,才能将自己的所学运用到实际当中去,这时候可以搞点实战案例来学习。

img

六、面试宝典

在这里插入图片描述

在这里插入图片描述

简历模板在这里插入图片描述
若有侵权,请联系删除
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值