1.什么是字符编码
人类在与计算机交互时,用的都是人类能读懂的字符,如中文字符、英文字符、日文字符等
而计算机只能识别二进制数,详解如下
二进制数即由0和1组成的数字,例如010010101010。计算机是基于电工作的,电的特性即高低电平,人类从逻辑层面将高电平对应为数字1,低电平对应为数字0,这直接决定了计算机可以识别的是由0和1组成的数字
毫无疑问,由人类的字符到计算机中的数字,必须经历一个过程,字符——>翻译——>数字。
翻译的过程必须参照一个特定的标准,该标准称之为字符编码表,该表上存放的就是字符与数字一一对应的关系。
字符编码中的编码指的是翻译或者转换的意思,即将人能理解的字符翻译成计算机能识别的数字
2.字符编码表的发展史 (了解)
字符编码的发展经历了三个重要的阶段,如下
阶段一:一家独大
现代计算机起源于美国,所以最先考虑仅仅是让计算机识别英文字符,于是诞生了ASCII表
ASCII表的特点:
1、只有英文字符与数字的一一对应关系
2、一个英文字符对应1Bytes,1Bytes=8bit,8bit最多包含256个数字,可以对应256个字符,足够表示所有英文字符
阶段二:诸侯割据、天下大乱
为了让计算机能够识别中文和英文,中国人定制了GBK
GBK表的特点:
1、只有中文字符、英文字符与数字的一一对应关系
2、一个英文字符对应1Bytes
一个中文字符对应2Bytes
补充说明:
1Bytes=8bit,8bit最多包含256个数字,可以对应256个字符,足够表示所有英文字符
2Bytes=16bit,16bit最多包含65536个数字,可以对应65536个字符,足够表示所有中文字符
每个国家都各自的字符,为让计算机能够识别自己国家的字符外加英文字符,各个国家都制定了自己的字符编码表
Shift_JIS表的特点:
1、只有日文字符、英文字符与数字的一一对应关系
Euc-kr表的特点:
1、只有韩文字符、英文字符与数字的一一对应关系
此时,美国人用的计算机里使用字符编码标准是ASCII、中国人用的计算机里使用字符编码标准是GBK、日本人用的计算机里使用字符编码标准是Shift_JIS。
字符编码发展到了这个阶段,可以用一句话概括:诸侯割据、天下大乱。
阶段三:分久必合
unicode于1990年开始研发,1994年正式公布,具备两大特点:
#1. 存在所有语言中的所有字符与数字的一一对应关系,即兼容万国字符
#2. 与传统的字符编码的二进制数都有对应关系,详解如下
很多地方或老的系统、应用软件仍会采用各种各样传统的编码,这是历史遗留问题。此处需要强调:软件是存放于硬盘的,而运行软件是要将软件加载到内存的,面对硬盘中存放的各种传统编码的软件,想让我们的计算机能够将它们全都正常运行而不出现乱码,内存中必须有一种兼容万国的编码,并且该编码需要与其他编码有相对应的映射/转换关系,这就是unicode的第二大特点产生的缘由
文本编辑器输入任何字符都是最新存在于内存中,是unicode编码的,存放于硬盘中,则可以转换成任意其他编码,只要该编码可以支持相应的字符
# 英文字符可以被ASCII识别
英文字符--->unciode格式的数字--->ASCII格式的数字
# 中文字符、英文字符可以被GBK识别
中文字符、英文字符--->unicode格式的数字--->gbk格式的数字
# 日文字符、英文字符可以被shift-JIS识别
日文字符、英文字符--->unicode格式的数字--->shift-JIS格式的数字
3.编码与解码
由字符转换成内存中的unicode,以及由unicode转换成其他编码的过程,都称为编码encode
由内存中的unicode转换成字符,以及由其他编码转换成unicode的过程,都称为解码decode
在诸多文件类型中,只有文本文件的内存是由字符组成的,因而文本文件的存取也涉及到字符编码的问题
4.utf-8的由来
注意:如果保存到硬盘的是GBK格式二进制,当初用户输入的字符只能是中文或英文,同理如果保存到硬盘的是Shift_JIS格式二进制,当初用户输入的字符只能是日文或英文……如果我们输入的字符中包含多国字符,那么该如何处理?
#多国字符—√—》内存(unicode格式的二进制)——X—》硬盘(GBK格式的二进制)
#多国字符—√—》内存(unicode格式的二进制)——X—》硬盘(Shift_JIS格式的二进制)
#多国字符—√—》内存(unicode格式的二进制)——√—》硬盘(???格式的二进制)
理论上是可以将内存中unicode格式的二进制直接存放于硬盘中的,但由于unicode固定使用两个字节来存储一个字符,如果多国字符中包含大量的英文字符时,使用unicode格式存放会额外占用一倍空间(英文字符其实只需要用一个字节存放即可),然而空间占用并不是最致命的问题,最致命地是当我们由内存写入硬盘时会额外耗费一倍的时间,所以将内存中的unicode二进制写入硬盘或者基于网络传输时必须将其转换成一种精简的格式,这种格式即utf-8(全称Unicode Transformation Format,即unicode的转换格式)
# 多国字符—√—》内存(unicode格式的二进制)——√—》硬盘(utf-8格式的二进制)
那为何在内存中不直接使用utf-8呢?
utf-8是不定长的:一个英文字符占1Bytes,一个中文字符占3Bytes,生僻字用更多的Bytes存储
也就意味着如果用户输入的字符是:你y好,在内存中需要先经历计算的过程:“你”应该用3Bytes,“y”应该用1Bytes,“好”应该用3Bytes,然后才能存储,所以内存中如果直接使用utf-8格式去存储字符,耗费的总时间=计算时间+存储时间,而内存中使用定长的unicode格式存储字符,就省去了计算时间,所以内存中使用unicode来存储字符会浪费空间,但是会提升速度,这是一种用空间换时间的方法
5.字符编码的应用
我们学习字符编码就是为了存取字符时不发生乱码问题:
#1、内存中固定使用unicode无论输入任何字符都不会发生乱码
#2、我们能够修改的是存/取硬盘的编码方式,如果编码设置不正确将会出现乱码问题。乱码问题分为两种:存乱了,读乱了
#2.1 存乱了:如果用户输入的内容中包含中文和日文字符,如果单纯以shift_JIS存,日文可以正常写入硬盘,而由于中文字符在shift_jis中没有找到对应关系而导致存乱了
#2.2 读乱了:如果硬盘中的数据是shift_JIS格式存储的,采GBK格式读入内存就读乱了
总结:
#1. 保证存的时候不乱:在由内存写入硬盘时,必须将编码格式设置为支持所输入字符的编码格式
#2. 保证存的时候不乱:在由硬盘读入内存时,必须采用与写入硬盘时相同的编码格式
扩展
"""
字符:在计算机和电信技术中,一个字符是一个单位的字形、类字形单位或符号的基本信息。即一个字符可以是一个中文汉字、一个英文字母、一个阿拉伯数字、一个标点符号等。
"""
ASCII 128个字符,用7位二进制表示(00000000-01111111即0x00-0x7F);
GB2312/GBK 中文字符2个字节,单个英文字符1字节
UTF-8 ascii码中的内容用1个字节保存,中文字符3个字节
python解释器执行文件
## python解释器执行文件的前两个阶段
执行py文件的前两个阶段就是python解释器读文本文件的过程,与文本编辑读文本文件的前两个阶段没人任何区别,要保证读不乱码,则必须将python解释器读文件时采用的编码方式设置为文件当初写入硬盘时的编码格式,如果没有设置,python解释器则才用默认的编码方式,在python3中默认为utf-8,在python2中默认为ASCII,我们可以通过指定文件头来修改默认的编码
在文件首行写入包含#号在内的以下内容
# coding: 当初文件写入硬盘时采用的编码格式
解释器会先用默认的编码方式读取文件的首行内容,由于首行是纯英文组成,而任何编码方式都可以识别英文字符。
## python解释器执行文件的第三个阶段
设置文件头的作用是保证运行python程序的前两个阶段不乱码,经过前两个阶段后py文件的内容都会以unicode格式存放于内存中。
在经历第三个阶段时开始识别python语法,当遇到特定的语法name = '上'(代码本身也都全都是unicode格式存的)时,需要申请内存空间来存储字符串'上',这就又涉及到应该以什么编码存储‘上’的问题了。
在Python3中,字符串类的值都是使用unicode格式来存储
由于Python2的盛行是早于unicode的,因此在Python2中是按照文件头指定的编码来存储字符串类型的值的(如果文件头中没有指定编码,那么解释器会按照它自己默认的编码方式来存储‘上’),所以,这就有可能导致乱码问题
# coding:utf-8
x = '上' # x的值为untf-8格式的二进制
print(x) # 打印操作是将x的值,即utf-8格式的二进制交给终端,当终端收到后发现并不是unicode(只有unicode才与字符有对应关系),所以终端会执行操作:utf-8二进制---解码-->unicode格式的二进制,解码的过程终端会采用自己默认的编码,而在pycharm的终端默认编码为utf-8、windows下的cmd终端的默认编码为gbk,所以该打印操作在pycharm中显示正常,而在windows下的cmd中则乱码
# 在windows下的cmd中运行效果如下
C:UsersAdministrator>python2 E:aaa.py
涓
python2后推出了一种补救措施,就是在字符串类型前加u,则会将字符串类型强制存储unicode,这就与python3保持一致了,对于unicode格式无论丢给任何终端进行打印,都可以直接对应字符不会出现乱码问题
# coding:utf-8
x = u'上' # 即便文件头为utf-8,x的值依然存成unicode
字符串encode编码与decode解码的使用
# 1、unicode格式------编码encode-------->其它编码格式
>>> x='上' # 在python3在'上'被存成unicode
>>> res=x.encode('utf-8')
>>> res,type(res) # unicode编码成了utf-8格式,而编码的结果为bytes类型,可以当作直接当作二进制去使用
(b'xe4xb8x8a', <class 'bytes'>)
# 2、其它编码格式------解码decode-------->unicode格式
>>> res.decode('utf-8')
'上'