对Python3编码的整理!!! py编码终极版

py编码终极版

 

提及python编码,真是句句心酸。算起来,反复折腾两个来月了。万幸的是,终于梳理清楚了。做为一个共产主义者,必定要分享给你们。若是你还在由于编码而头痛,那么赶忙跟着我我们一块儿来揭开py编码的真相吧!html

一 什么是编码?

基本概念很简单。首先,咱们从一段信息即消息提及,消息以人类能够理解、易懂的表示存在。我打算将这种表示称为“明文”(plain text)。对于说英语的人,纸张上打印的或屏幕上显示的英文单词都算做明文。python

其次,咱们须要能将明文表示的消息转成另外某种表示,咱们还须要能将编码文本转回成明文。从明文到编码文本的转换称为“编码”,从编码文本又转回成明文则为“解码”。linux

复制代码
编码问题是个大问题,若是不完全解决,它就会像隐藏在丛林中的小蛇,时不时地咬你一口。
    那么到底什么是编码呢?

    //ASCII

    记住一句话:计算机中的全部数据,不管是文字、图片、视频、仍是音频文件,本质上最终都是按照相似 01010101 的二进制存储的。
再说简单点,计算机只懂二进制数字! 因此,目的明确了:如何将咱们能识别的符号惟一的与一组二进制数字对应上?因而美利坚的同志想到经过一个电平的高低状态来代指0或1,
八个电平作为一组就能够表示出 256种不一样状态,每种状态就惟一对应一个字符,好比A--->00010001,而英文只有26个字符,算上一些特殊字符和数字,128个状态也够
用了;每一个电平称为一个比特为,约定8个比特位构成一个字节,这样计算机就能够用127个不一样字节来存储英语的文字了。这就是ASCII编码。 扩展ANSI编码 刚才说了,最开始,一个字节有八位,可是最高位没用上,默认为0;后来为了计算机也能够表示拉丁文,就将最后一位也用上了, 从128到255的字符集对应拉丁文啦。至此,一个字节就用满了! //GB2312 计算机漂洋过海来到中国后,问题来了,计算机不认识中文,固然也无法显示中文;并且一个字节全部状态都被占满了,万恶的帝国主义亡
我之心不死啊!我党也是棒,自力更生,本身重写一张表,直接生猛地将扩展的第八位对应拉丁文所有删掉,规定一个小于127的字符的意
义与原来相同,但两个大于127的字符连在一块儿时,就表示一个汉字,前面的一个字节(他称之为高字节)从0xA1用到0xF7,后面一个字节
(低字节)从0xA1到0xFE,这样咱们就能够组合出大约7000多个简体汉字了;这种汉字方案叫作 “GB2312”。GB2312 是对 ASCII 的中文扩展。 //GBK 和 GB18030编码 可是汉字太多了,GB2312也不够用,因而规定:只要第一个字节是大于127就固定表示这是一个汉字的开始,无论后面跟的是否是扩展字符集里的
内容。结果扩展以后的编码方案被称为 GBK 标准,GBK 包括了 GB2312 的全部内容,同时又增长了近20000个新的汉字(包括繁体字)和符号。 //UNICODE编码: 不少其它国家都搞出本身的编码标准,彼此间却相互不支持。这就带来了不少问题。因而,国际标谁化组织为了统一编码:提出了标准编码准
则:UNICODE 。 UNICODE是用两个字节来表示为一个字符,它总共能够组合出65535不一样的字符,这足以覆盖世界上全部符号(包括甲骨文) //utf8: unicode都一统天下了,为何还要有一个utf8的编码呢? 你们想,对于英文世界的人们来说,一个字节彻底够了,好比要存储A,原本00010001就能够了,如今吃上了unicode的大锅饭, 得用两个字节:00000000 00010001才行,浪费太严重! 基于此,美利坚的科学家们提出了天才的想法:utf8. UTF-8(8-bit Unicode Transformation Format)是一种针对Unicode的可变长度字符编码,它可使用1~4个字节表示一个符号,根据
不一样的符号而变化字节长度,当字符在ASCII码的范围时,就用一个字节表示,因此是兼容ASCII编码的。 这样显著的好处是,虽然在咱们内存中的数据都是unicode,但当数据要保存到磁盘或者用于网络传输时,直接使用unicode就远不如utf8省空间啦! 这也是为何utf8是咱们的推荐编码方式。 Unicode与utf8的关系: 一言以蔽之:Unicode是内存编码表示方案(是规范),而UTF是如何保存和传输Unicode的方案(是实现)这也是UTF与Unicode的区别。
复制代码

补充:utf8是如何节约硬盘和流量的

1
s = "I'm 苑昊"

你看到的unicode字符集是这样的编码表:json

I  0049
'  0027
m  006d
   0020
苑 82d1
昊 660a

每个字符对应一个十六进制数字。
计算机只懂二进制,所以,严格按照unicode的方式(UCS-2),应该这样存储:网络

I      00000000 01001001
'      00000000 00100111
m      00000000 01101101
       00000000 00100000
苑     10000010 11010001
昊     01100110 00001010

这个字符串总共占用了12个字节,可是对比中英文的二进制码,能够发现,英文前9位都是0!浪费啊,浪费硬盘,浪费流量。怎么办?UTF8:编辑器

I    01001001
'    00100111
m    01101101
     00100000
苑   11101000 10001011 10010001
昊   11100110 10011000 10001010

utf8用了10个字节,对比unicode,少了两个,由于咱们的程序英文会远多于中文,因此空间会提升不少!函数

记住:一切都是为了节省你的硬盘和流量。  post

二 py2的string编码

在py2中,有两种字符串类型:str类型和unicode类型;注意,这仅仅是两个名字,python定义的两个名字,关键是这两种数据类型在程序运行时存在内存地址的是什么?编码

咱们来看一下:url

1
2
3
4
5
6
7
8
9
10
#coding:utf8
 
s1 = '苑'
 
print  type (s1)  # <type 'str'>
print  repr (s1)  #'\xe8\x8b\x91
 
s2 = u '苑'
print  type (s2)  # <type 'unicode'>
print  repr (s2)  # u'\u82d1'

内置函数repr能够帮咱们在这里显示存储内容。原来,str和unicode分别存的是字节数据和unicode数据;那么两种数据之间是什么关心呢?如何转换呢?这里就涉及到编码(encode)和解码(decode)了

复制代码
s1=u'苑'
print repr(s1) #u'\u82d1'

b=s1.encode('utf8')
print b
print type(b)  #<type 'str'>
print repr(b)  #'\xe8\x8b\x91'

s2='苑昊'
u=s2.decode('utf8')
print u        # 苑昊
print type(u)  # <type 'unicode'>
print repr(u)  # u'\u82d1\u660a'

#注意
u2=s2.decode('gbk')
print u2  #鑻戞槉

print len('苑昊') #6
复制代码

不管是utf8仍是gbk都只是一种编码规则,一种把unicode数据编码成字节数据的规则,因此utf8编码的字节必定要用utf8的规则解码,不然就会出现乱码或者报错的状况。

py2编码的特点:

1
2
3
4
5
6
7
8
9
#coding:utf8
 
print  '苑昊'  #  苑昊   
print  repr ( '苑昊' ) #'\xe8\x8b\x91\xe6\x98\x8a'
 
print  (u "hello" + "yuan" )
 
#print (u'苑昊'+'最帅')   #UnicodeDecodeError: 'ascii' codec can't decode byte 0xe6
                          # in position 0: ordinal not in range(128)

Python 2 悄悄掩盖掉了 byte 到 unicode 的转换,只要数据所有是 ASCII 的话,全部的转换都是正确的,一旦一个非 ASCII 字符偷偷进入你的程序,那么默认的解码将会失效,从而形成 UnicodeDecodeError 的错误。py2编码让程序在处理 ASCII 的时候更加简单。你复出的代价就是在处理非 ASCII 的时候将会失败。

三 py3的string编码

python3 renamed the unicode type to str ,the old str type has been replaced by bytes.

 py3也有两种数据类型:str和bytes;  str类型存unicode数据,bytse类型存bytes数据,与py2比只是换了一下名字而已。

 

复制代码
import json

s='苑昊'
print(type(s))       #<class 'str'>
print(json.dumps(s)) #  "\u82d1\u660a"

b=s.encode('utf8')
print(type(b))      # <class 'bytes'>
print(b)            # b'\xe8\x8b\x91\xe6\x98\x8a'


u=b.decode('utf8')
print(type(u))       #<class 'str'>
print(u)             #苑昊
print(json.dumps(u)) #"\u82d1\u660a"


print(len('苑昊')) # 2
复制代码

 

 

py3的编码哲学:

Python 3最重要的新特性大概要算是对文本和二进制数据做了更为清晰的区分,再也不会对bytes字节串进行自动解码。文本老是Unicode,由str类型表示,二进制数据则由bytes类型表示。Python 3不会以任意隐式的方式混用str和bytes,正是这使得二者的区分特别清晰。你不能拼接字符串和字节包,也没法在字节包里搜索字符串(反之亦然),也不能将字符串传入参数为字节包的函数(反之亦然)。

1
2
#print('alvin'+u'yuan')#字节串和unicode链接 py2:alvinyuan
print (b 'alvin' + 'yuan' ) #字节串和unicode链接 py3:报错 can't concat bytes to str

注意:不管py2,仍是py3,与明文直接对应的就是unicode数据,打印unicode数据就会显示相应的明文(包括英文和中文)

四 文件从磁盘到内存的编码(******)

说到这,才来到咱们的重点!

抛开执行执行程序,请问你们,文本编辑器你们都是用过吧,若是不懂是什么,那么word总用过吧,ok,当咱们在word上编辑文字的时候,不论是中文仍是英文,计算机都是不认识的,那么在保存以前数据是经过什么形式存在内存的呢?yes,就是unicode数据,为何要存unicode数据,这是由于它的名字最屌:万国码!解释起来就是不管英文,中文,日文,拉丁文,世界上的任何字符它都有惟一编码对应,因此兼容性是最好的。

好,那当咱们保存了存到磁盘上的数据又是什么呢?

答案是经过某种编码方式编码的bytes字节串。好比utf8---一种可变长编码,很好的节省了空间;固然还有历史产物的gbk编码等等。因而,在咱们的文本编辑器软件都有默认的保存文件的编码方式,好比utf8,好比gbk。当咱们点击保存的时候,这些编辑软件已经"默默地"帮咱们作了编码工做。

那当咱们再打开这个文件时,软件又默默地给咱们作了解码的工做,将数据再解码成unicode,而后就能够呈现明文给用户了!因此,unicode是离用户更近的数据,bytes是离计算机更近的数据。

说了这么多,和咱们程序执行有什么关系呢?

先明确一个概念:py解释器自己就是一个软件,一个相似于文本编辑器同样的软件!

如今让咱们一块儿还原一个py文件从建立到执行的编码过程:

打开pycharm,建立hello.py文件,写入

ret=1+1
s='苑昊'
print(s)

      当咱们保存的的时候,hello.py文件就以pycharm默认的编码方式保存到了磁盘;关闭文件后再打开,pycharm就再以默认的编码方式对该文件打开后读到的内容进行解码,转成unicode到内存咱们就看到了咱们的明文;

      而若是咱们点击运行按钮或者在命令行运行该文件时,py解释器这个软件就会被调用,打开文件,而后解码存在磁盘上的bytes数据成unicode数据,这个过程和编辑器是同样的,不一样的是解释器会再将这些unicode数据翻译成C代码再转成二进制的数据流,最后经过控制操做系统调用cpu来执行这些二进制数据,整个过程才算结束。

那么问题来了,咱们的文本编辑器有本身默认的编码解码方式,咱们的解释器有吗?

固然有啦,py2默认ASCII码,py3默认的utf8,能够经过以下方式查询

1
2
import  sys
print (sys.getdefaultencoding())

你们还记得这个声明吗?

1
#coding:utf8

是的,这就是由于若是py2解释器去执行一个utf8编码的文件,就会以默认地ASCII去解码utf8,一旦程序中有中文,天然就解码错误了,因此咱们在文件开头位置声明 #coding:utf8,其实就是告诉解释器,你不要以默认的编码方式去解码这个文件,而是以utf8来解码。而py3的解释器由于默认utf8编码,因此就方便不少了。

                   

注意:咱们上面讲的string编码是在cpu执行程序时的存储状态,是另一个过程,不要混淆!

五 常见的编码问题

1 cmd下的乱码问题

hello.py

1
2
#coding:utf8
print  ( '苑昊' )

文件保存时的编码也为utf8。

思考:为何在IDE下用2或3执行都没问题,在cmd.exe下3正确,2乱码呢?

      咱们在win下的终端即cmd.exe去执行,你们注意,cmd.exe自己也一个软件;当咱们python2 hello.py时,python2解释器(默认ASCII编码)去按声明的utf8编码文件,而文件又是utf8保存的,因此没问题;问题出在当咱们print'苑昊'时,解释器这边正常执行,也不会报错,只是print的内容会传递给cmd.exe用来显示,而在py2里这个内容就是utf8编码的字节数据,可这个软件默认的编码解码方式是GBK,因此cmd.exe用GBK的解码方式去解码utf8天然会乱码。

py3正确的缘由是传递给cmd的是unicode数据,cmd.exe能够识别内容,因此显示没问题。

明白原理了,修改就有不少方式,好比:

1
print  (u '苑昊' )

改为这样后,cmd下用2也不会有问题了。

2 open()中的编码问题

建立一个hello文本,保存成utf8:

苑昊,你最帅!

同目录下建立一个index.py

f=open('hello')
print(f.read())

为何 在linux下,结果正常:苑昊,在win下,乱码:鑻戞槉(py3解释器)?

由于你的win的操做系统安装时是默认的gbk编码,而linux操做系统默认的是utf8编码;

当执行open函数时,调用的是操做系统打开文件,操做系统用默认的gbk编码去解码utf8的文件,天然乱码。

解决办法:

f=open('hello',encoding='utf8')
print(f.read())

若是你的文件保存的是gbk编码,在win 下就不用指定encoding了。

另外,若是你的win上不须要指定给操做系统encoding='utf8',那就是你安装时就是默认的utf8编码或者已经经过命令修改为了utf8编码。

注意:open这个函数在py2里和py3中是不一样的,py3中有了一个encoding=None参数。

相关文章
相关标签/搜索