python字符编码
什么是字符编码
计算机中所有的文件(文本、音频、视频等)都是存放在硬盘上的,更确切的说是以二进制字节存放在硬盘上。
这些文件中文本文件比较特殊。文本文件用来记录文本数据,也就是人类识别的字符,比如中文方块字、英文字母等。
因为计算机是只能识别二进制,所以这些字符存储在硬盘上肯定有一个翻译的过程,即将人类的字符翻译成计算机认识的二进制。这个翻译的过程就是所谓的字符编码。
什么是字符编码表
我们知道文本需要编码后才能存储在硬盘上,那该如何编码呢?
字符编码就和密码的解码表差不多,按照一定的规则,将不同的字符对应为不同的二进制数。不同的对应规则就是不同的字符编码表。
历史上美国人最早大规模使用计算机,为了解决他们使用的英文字符存储问题,他们制定了ascii码表
后来中国开始使用计算机,我们需要将汉字和英文存储在硬盘上,我们制定了GBK
日本人使用计算机将日文存储在硬盘上,于是他们制定了 Shift_JIS 。
# ASCII表的特点:
1、只有英文字符与数字的一一对应关系
2、一个英文字符对应1Bytes,1Bytes=8bit,8bit最多包含256个数字,可以对应256个字符,足够表示所有英文字符
# GBK表的特点:
1、只有中文字符、英文字符与数字的一一对应关系
2、一个英文字符对应1Bytes
一个中文字符对应2Bytes
补充说明:
1Bytes=8bit,8bit最多包含256个数字,可以对应256个字符,足够表示所有英文字符
2Bytes=16bit,16bit最多包含65536个数字,可以对应65536个字符,足够表示所有中文字符
文本编辑器存取文件的原理
# 文本文件内容全都为字符,无论存取都涉及到字符编码问题,如果使用的是ascii编码
#1、存文本文件
人类通过文本编辑器输入的字符会被转化成ASCII格式的二进制存放于内存中,如果需要永久保存,则直接将内存中的ASCII格式的二进制写入硬盘
#2、读文本文件
直接将硬盘中的ASCII格式的二进制读入内存,然后通过ASCII表反解成对应字符
# 如果使用的是 gbk编码
# 存文本文件
那么人类通过文本编辑器输入的字符会被转化成GBK格式的二进制存放于内存中,如果需要永久保存,则直接将内存中的GBK格式的二进制写入硬盘
#2、读文本文件
直接将硬盘中的GBK格式的二进制读入内存,然后通过GBK表反解成对应字符
按理说这样各国都可以使用计算机了,完美的解决了字符编码问题。
但是我要知道这个世界是需要交流的,按照目前的编码条件,国家之间是不能通过计算机通信的,因为每个国家的编码都不一样,这样的结果会导致字符显示乱码的问题。
Unicode
为了解决这个交流问题,出现了万国码,即Unicode。Unicode有两个特点:
- 存在所有语言中的所有字符与数字的一一对应关系,即兼容万国字符
- 与传统的字符编码的二进制数都有对应关系(很重要)
很多地方或老的系统、应用软件仍会采用各种各样传统的编码,这是历史遗留问题。
此处需要强调:软件是存放于硬盘的,而运行软件是要将软件加载到内存的;
面对硬盘中存放的各种传统编码的软件,想让我们的计算机能够将它们全都正常运行而不出现乱码,内存中必须有一种兼容万国的编码,并且该编码需要与其他编码有相对应的映射/转换关系,这就是unicode第二大特点产生的缘由
文本编辑器输入任何字符在内存中都是Unicode编码的;
需要存放到硬盘时,则可以转换成任意其他编码,只要该编码可以支持相应的字符;
# 英文字符可以被ASCII识别
英文字符--->unciode格式的二进制--->ASCII格式的二进制
# 中文字符、英文字符可以被GBK识别
中文字符、英文字符--->unicode格式的二进制--->gbk格式的二进制
# 日文字符、英文字符可以被shift-JIS识别
日文字符、英文字符--->unicode格式的二进制--->shift-JIS格式的二进制
UTF-8
Unicode解决了万国字符和二进制的对应关系,但是使用Unicode表示一个字符,太浪费空间。例如:利用Unicode表示“Python”需要12个字节才能表示,比原来ASCII表示增加了1倍。
由于计算机的内存比较大,并且字符串在内容中表示时也不会特别大,所以内容可以使用Unicode来处理,但是存储和网络传输时一般数据都会非常多,那么增加1倍将是无法容忍的!!!
为了解决存储和网络传输的问题,出现了Unicode Transformation Format,学术名UTF,即:对Unicode中的进行转换,以便于在存储和网络传输时可以节省空间!
- UTF-8: 使用1、2、3、4个字节表示所有字符;优先使用1个字符、无法满足则使增加一个字节,最多4个字节。英文占1个字节、欧洲语系占2个、东亚占3个,其它及特殊字符占4个。
- UTF-16: 使用2、4个字节表示所有字符;优先使用2个字节,否则使用4个字节表示。
- UTF-32: 使用4个字节表示所有字符。
那为何在内存中不直接使用utf-8呢?
utf-8是不定长的:一个英文字符占1Bytes,一个中文字符占3Bytes,生僻字用更多的Bytes存储
# 原因1:
也就意味着如果用户输入的字符是:你y好,在内存中需要先经历计算的过程:“你”应该用3Bytes,“y”应该用1Bytes,“好”应该用3Bytes,然后才能存储,所以内存中如果直接使用utf-8格式去存储字符,耗费的总时间=计算时间+存储时间,而内存中使用定长的unicode格式存储字符,就省去了计算时间,所以内存中使用unicode来存储字符会浪费空间,但是会提升速度,这是一种用空间换时间的方法
# 原因2:
历史遗留问题,硬盘中存在很多其他编码方式的文件,内存如果此时使用utf8就会导致乱码问题
总结:UTF 是为Unicode编码 设计 的一种 在存储 和传输时节省空间的编码方案。
字符编码使用
我们学习字符编码就是为了存取字符时不发生乱码问题:
乱码的唯一原因:将内存中的二进制数据转换为我们希望的字符时,没有使用正确的转换规则。
#1、内存中固定使用unicode时无论输入任何字符都不会发生乱码
#2、我们能够修改的是存/取硬盘的编码方式,如果编码设置不正确将会出现乱码问题。乱码问题分为两种:存乱了,读乱了
#2.1 存乱了:如果用户输入的内容中包含中文和日文字符,如果单纯以shift_JIS存,日文可以正常写入硬盘,而由于中文字符在shift_jis中没有找到对应关系而导致存乱了
#2.2 读乱了:如果硬盘中的数据是shift_JIS格式存储的,采GBK格式读入内存就读乱了
总结:
#1. 保证存的时候不乱:在由内存写入硬盘时,必须将编码格式设置为支持所输入字符的编码格式
#2. 保证取的时候不乱:在由硬盘读入内存时,必须采用与写入硬盘时相同的编码格式
温故知新
2.2 文本编辑器读取文件内容的流程
#阶段1、启动一个文件编辑器(文本编辑器如nodepad++,pycharm,word)
#阶段2、文件编辑器会将文件内容从硬盘读入内存
#阶段3、文本编辑器会将刚刚读入内存中的内容显示到屏幕上
2.3 python解释器执行文件的流程
以python test.py为例,执行流程如下
#阶段1、启动python解释器,此时就相当于启动了一个文本编辑器
#阶段2、python解释器相当于文本编辑器,从硬盘上将test.py的内容读入到内存中
#阶段3、python解释器解释执行刚刚读入的内存的内容,开始识别python语法
文本编辑器notepad++存取文本文件
打开notepad++,修改编码为shift-JIS输入一行中文和一行日文,保存文件
重新打开notepad++,发现编码默认为ascii,而输入的文本已经乱码,只有编码修改为shift-JIS才不乱码
python解释器执行文件的前两个阶段
执行py文件的前两个阶段就是python解释器读文本文件的过程,与文本编辑读文本文件的前两个阶段没人任何区别,要保证读不乱码,则必须将python解释器读文件时采用的编码方式设置为文件当初写入硬盘时的编码格式,如果没有设置,python解释器则才用默认的编码方式,在Python3中默认读采用为Utf-8,在Python2中默认为ASCII,我们可以通过指定文件头来修改默认读文件的编码
- 在文件首行写入包含#号在内的以下内容
# codeing: utf-8
解释器会先用默认的编码方式读取文件的首行内容,由于首行是纯英文组成,而任何编码方式都可以识别英文字符。
python解释器执行文件的第三个阶段
设置文件头的作用是保证运行python程序的前两个阶段不乱码,经过前两个阶段后py文件的内容都会以写入时的编码格式存放于内存中。
在经历第三个阶段时开始识别python语法,当遇到特定的语法name = '上'(代码本身也都全都是Unicode格式存的)时,需要申请内存空间来存储字符串'上',这就又涉及到应该以什么编码存储'上'的问题了。
在Python3中,字符串类的值都是使用Unicode格式来存储
# python3中,python解释器会将utf8的编码的二进制数据帮我们转成Unicode格式的二进制。
# 执行打印时,因为Unicode存在的第二个特点,可以映射任意格式的编码方式,可以找到对应的字符,所在在任何终端都不会乱码。
由于Python2的盛行是早于Unicode的,因此在Python2中是按照文件头指定的编码来存储字符串类型的值的(如果文件头中没有指定编码,那么解释器会按照它自己默认的编码方式来存储'上'),所以,这就有可能导致乱码问题.
# coding:utf-8
x = '上' # x的值为untf-8格式的二进制
print(x)
# 打印操作是将x的值,即utf-8格式的二进制交给终端,终端收到后会按照自己默认的方式解码找到对应字符
# 在pycharm中,默认的解码方式是utf-8,所以会找到对应的字符,不会出现乱码问题;
# 在windows的cmd上,默认解码方式是gbk,将utf-8编码的二进制数据按照gbk格式来找对应编码肯定找不到,于是就乱码了。
# python2上使用字符串,定义字符串是前面加u,如: name = u'小明'
字符串encode编码与decode解码的使用
# 1、unicode格式------编码encode-------->其它编码格式
>>> x='上' # 在python3在'上'被存成unicode
>>> res=x.encode('utf-8')
>>> res,type(res) # unicode编码成了utf-8格式,而编码的结果为bytes类型,可以当作直接当作二进制去使用
(b'\xe4\xb8\x8a', <class 'bytes'>)
# 2、其它编码格式------解码decode-------->unicode格式
>>> res.decode('utf-8')
'上'
总结
1、内存固定使用Unicode,我们可以改变的是存入硬盘采用的编码格式
在中国使用计算机,英文+汉字-》unicode-》gbk
在日本使用计算机,英文+日文-》unicode-》shift-jis
通过用,万国字符》-unicode-》utf-8
2、编码互转问题
1 Unicoede可以和任意编码方式互转
2 utf-8不能和其他编码格式互转(Unicode除外)
3、文本文件存取乱码问题
存乱了:解决方法是,编码格式应该设置成支持文件内字符串的编码格式
取乱了:解决方法是,文件是以什么编码格式存如硬盘的,就应该以什么编码格式读入内存
4、py2和py3字符编码(读文件)的区别
python3默认的编码是utf-8,且会自动将utf-8转为Unicode
python2默认的编码是ascii,可以通过文件头的方式指定读文件采用的编码,但不会帮忙转Unicode
5、python2中使用字符串变量需要加u,如:name = u'小明',转为Unicode
#总结的总结:
1、使用python3解释器,代码脚本使用utf-8的编码格式保存到硬盘
2、使用python2解释器,代码脚本首行需要加 codeing:编码格式(写这个脚本时保存的编码格式,不知道的时候只能暴力尝试),代码中的字符串前加'u'避免跨终端乱码问题
3、python3脚本如果在python2解释器上运行时,首行加头,字符串变量加'u'