py编码

编码:是一套法则,使用该法则能够对自然语言的字符的一个集合(如字母表或音节表),与其他东西的一个集合(如号码或电脉冲)进行配对。(我的理解就是从人能看懂的到人看不懂的是编码,从人看不懂的到人能看懂的是解码)

ASCII

计算机中的所有的数据,不论是文字、图片、视频、音频等文件,本质上都是按照010101的二进制存储的,计算机只懂得二进制数字。
因此我们如何能我们能识别的符号唯一的与一组二进制数字对应上呢,于是美利坚的朋友通过一个电平的高低状态来代指0或1,八个电平作为一组就可以表示出256种不同的状态,每种状态就唯一对应一个字符,比如A—-》00010001,而英文只有26个字符,算上一些特殊字符和数字,128哥状态就够了,每一个电平称为一个比特位,8个比特位构成一个字节,这样计算机就可以用127个不同字节来存储英语的文字了。这就是ASCII编码。

扩展ANSII编码

刚才说了,最开始,一个字节有8位,最高位没有用上,默认为0,后来为了计算机可以表示拉丁文,就将最后一位也用上了,从128到255的字符集对应拉丁文啦,至此,一个字节就用满了

GB2312

计算机来多中国后,问题出现了,计算机不认识中文,当然就没法显示中文,而且一个字节的所有状态都被占满了,为了表示中文,国人重写了一张表,直接把扩展的第8位对应的拉丁文全部删掉,规定一个小于127的字符的意义与原来相同,但两个大于127的字符连在一起时,就表示一个汉字,前面的一个字节(高字节)从0xA1用到0xF7,后面一个字节(低字节)从0xA1到0xFE,这样我们就可以组合出大约7000多哥简体汉字了,这种汉字方案叫做”GB2312”. GB2312是对ASCII的中文扩展。

GBK和GB18030编码

但是汉字太多了,GB2312也不够用,于是规定:只要第一个字节是大于127就固定表示这是一个汉字的开始,不管后面跟的是不是扩展字符集里的内容,结果扩展之后的编码方案被称为GBK标准,GBK包括了GB2312的所有内容,同时又增加了近20000个新的汉字(包括繁体字)和符号。

UNICODE编码

很多其它国家都搞出了自己的编码标准,彼此间却相互不支持,这就带来了很多问题,于是,国际标准化组织为了统一编码:提出了标准编码表
则:UNCODE
UNCODE是用两个字节表示为一个字符,它总共可以组合出65535个不同的字符,这足以覆盖世界上所有符号(包括甲骨文)

UTF-8

unicode都一统天下了,为什么还要有个utf-8的编码呢?
大家想,对于英文世界的人们来讲,一个字节就够了,比如要存储A,本来0100 0001就可以了,现在吃上了UNICODE的大锅饭,得用两个字节:00000000 01000001才行,浪费太严重了!基于此,美利坚的科学家提出了天才的想法:utf8,UTF-8(8-bit Unicode Transformation Format)是一种针对Unicode的可变长度字符编码,它可以使用1~4个字节表示一个符号,根据不同符号而变化字节长度,当字符在ASCII码的范围时,就用一个字节表示,所以是兼容ASCII编码的。
这样显著的好处是:虽然在我们内存中的数据都是unicode,但当数据要保存到磁盘或者用于网络传输时,直接使用unicode就远不如utf8省空间啦!这也是我们推荐使用utf8的编码方式。
UNICODE与utf8的关系:
一言以蔽之:Unicode是内存编码表示方案(是规范),而UTF8是如何保存和传输Unicode的方案(是实现)这也是UTF8与Unicode的区别

补充:utf8是如何节约硬盘和流量的
s=”I’m 唐书培” (python3默认的str是unicode的) 也就是说s=”I’m 唐书培” 与s=u”I’m 唐书培”是一样的
因此这些字符串对应unicode编码16进制 和二进制表示为如下:

I  0049
'  0027
m  006d
   002055104e6657f9

具体转换过程为:
st = u'唐'.encode('unicode-escape')
print(st)
b'\\u5510'
st = st.decode("utf-8")
print(st)
\u5510
st = st.replace(r"\u","")
print(st)
5510

计算机只懂得二进制,故上面字符对应的二进制表示为:
I   00000000 01001001
'   00000000 00100111
m   00000000 01101101
    00000000 0010000001010101 0001000001001110 0110011001010111 11111001

这个s字符串如果用unicode编码方式在计算机存储共总占用了14个字节,但是对比中英文的二进制码,可以发现,英文前9位都是0,存储浪费空间,传输浪费流量,怎么办,UTF8上场啦。。。

I   01001001
'   00100111
m   01101101
    00100000
唐  11100101 10010100 10010000
书  11100100 10111001 10100110
培  11100101 10011111 10111001

如果用utf8编码方式在计算机中存储共占用13个字节,比unicode少了一个,由于在编程程序中英文字符远多于中文支付,所以空间会省很多。。。

二:python2的string编码

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

>>> s1 = '唐'
>>> print type(s1)
<type 'str'>
>>> print repr(s1)
'\xe5\x94\x90'
>>> 
>>> 
>>> s2 = u'唐'
>>> print(type(s2))
<type 'unicode'>
>>> print(repr(s2))
u'\u5510'

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

Python 2.7.7 (default, Apr  3 2017, 05:52:27) 
[GCC 4.4.7 20120313 (Red Hat 4.4.7-17)] on linux2
Type "help", "copyright", "credits" or "license" for more informat
>>> s1 = '唐'
>>> print type(s1)
<type 'str'>
>>> print repr(s1)
'\xe5\x94\x90'
>>> 
>>> 
>>> s2 = u'唐'
>>> print(s2)>>> print(repr(s2))
u'\u5510'
>>> print(type(s2))
<type 'unicode'>
>>> 
>>> s1 = u'唐'
>>> print(repr(s1))
u'\u5510'
>>> b = s1.encode('utf8')
>>> print(b)>>> print(type(b))
<type 'str'>
>>> print(repr(b))
'\xe5\x94\x90'

>>>注意:
>>> s1 = '唐书培' (python2中汉字字符默认utf8编码)
>>> u = s1.decode('utf8')
>>> print u
唐书培
>>> print(type(u))
<type 'unicode'>
>>> print(repr(u))
u'\u5510\u4e66\u57f9'
>>> u1 = s1.decode('gbk')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
UnicodeDecodeError: 'gbk' codec can't decode byte 0xb9 in position 8: incomplete multibyte sequence

无论是utf8还是gbk都只是一种编码规则,一种把unicode数据编码成字节数据的规则,所以utf8编码的字节一定要用utf8的规则解码,否则就会出现乱码或者报错的情况。

三 python3的string编码

python3 renamed the unicode type to str, the old str type has been replaced by bytes.
python3也有两种数据类型:str和bytes; str类型存unicode数据,bytse类型存bytes数据,与py2比只是换了一下名字而已。

>>> import json
>>> s = '唐书培'
>>> print(type(s))
<class 'str'>
>>> s1 = json.dumps(s)
>>> print(s1)
"\u5510\u4e66\u57f9"
>>> print(type(s1))
<class 'str'>
>>> print(json.dumps(s))
"\u5510\u4e66\u57f9"
>>> 
>>> 
>>> b = s.encode('utf8')
>>> print(type(b))
<class 'bytes'>
>>> print(b)
b'\xe5\x94\x90\xe4\xb9\xa6\xe5\x9f\xb9'


>>> u = b.decode('utf8')
>>> print(type(u))
<class 'str'>
>>> print(u)
唐书培
>>> print(json.dumps(u))
"\u5510\u4e66\u57f9"
>>> print(len(u))
3
>>> print(len('唐书培'))
3

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

Python 2.7.7 (default, Apr  3 2017, 05:52:27) 
[GCC 4.4.7 20120313 (Red Hat 4.4.7-17)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> print('cntsp'+u'nihao')
cntspnihao
>>> s1 = 'cntsp'
>>> print(type(s1))
<type 'str'>
>>> s2 = u'cntsp'
>>> print(type(s2))
<type 'unicode'>


Python 3.5.0 (default, Oct 30 2017, 19:29:56) 
[GCC 4.4.7 20120313 (Red Hat 4.4.7-18)] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> print(b'cntsp'+u'nihao')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: can't concat bytes to str
>>> s1 = 'nihao'
>>> print(type(s1))
<class 'str'>
>>> s2 = u'nihao'
>>> print(type(s2))
<class 'str'>

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

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

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

抛开执行程序,请问大家,文本编辑器大家都是用过的吧,如果不懂是什么,那么word总用过吧,ok,当我们在word上编辑文字的时候,不管是中文还是英文,计算机都是不认识的,那么在保存之前数据是通过什么形式存在内存的呢?yes,就是unicode数据,为什么要存unicode数据,因为它的名字最屌:万国码!解释起来就是无论地球上的哪一种语言,不管是英文、中文、德文、日文、拉丁文、甚至甲骨文,unicode都有唯一的编码对应,所以兼容性是最好的。
然而,当我们保存了数据从内存写入磁盘上时有时以何种编码方式以何种形式存在呢?
答案:是通过某种编码方式编码的bytes字节串存储在磁盘上的,编码方式比如:utf8 一种可变长编码,很好的节省了空间;当然还有历史产物的gbk编码等等,于是在我们的文本编辑器软件都有默认的保存文件的编码方式,比如utf8,比如gbk。当我们点击保存的时候,这些编辑软件已经默默地帮我们做了编码工作了,那当我们再打开这个文件时,软键又默默地给我们做了解码的工作,将数据在解码成unicode,然后就可以呈现明文给用户了,所以,unicode是离用户更近的数据,bytes是离计算机更近的数据。说了这么多,和我们程序执行有什么关系呢?
先明确一个概念:py解析器本身就是一个软件,一个类似于文本编辑器一样的软件!
现在让我们一起还原一个py文件从创建到执行的编码过程:
在pycharm中,创建一个hello.py文件,写入:

print("hello世界你好!")
s = 'hello世界你好!'
print(type(s))

s1 = s.encode('utf8')
print(s1)
对应输出为:
>>>
>>>hello世界你好!
>>><class 'str'>
>>>b'hello\xe4\xb8\x96\xe7\x95\x8c\xe4\xbd\xa0\xe5\xa5\xbd\xef\xbc\x81'

当我们保存的时候,hello.py文件就以pycharm默认的编码方式保存到了磁盘;关闭文件后再打开,pycharm就再以默认的编码方式对该文件打开后读到的内容进行编码,转成unicode到内存我们就看到了我们的明文;
而如果我们点击运行按钮后者在命令行运行该文件时,py解释器这个软件就会被调用,打开文件,然后解码存在磁盘上的bytes数据成unicode数据,这个过程和编辑器是一样的,不同的是解释器会在将这些unicode数据翻译成C代码再转成二进制的数据流,最后通过控制操作系统调用cpu来执行这些二进制数据,整个过程才算结束。
那么问题来了,我们的文本编辑器有自己默认的编码解码方式,我们的解释器有吗?
这是肯定的,python2默认是ASCII码,python3默认的utf8,可以通过如下方式查询

import sys
print(sys.getdefaultencoding())

大家还记得这个声明吗?
#--coding:utf8--

由于py2中解析器默认的编解码是ASCII,如果py2解释器去执行一个utf8编码的文件,就会以默认的解码方式ASCII去解码utf8,一旦程序中有中文,自然解码错误,所以我们在文件的开头位置声明,#--coding:utf8-- ,其实就是告诉解释器,你不要以默认的编码方式去解码这个文件,要以这个文件原有的编码方式utf8来解码它,由于python3中解释器默认utf8编码,所以可以不用写这个声明。
python编码

py编码终极版


文章作者: 阿培
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 阿培 !
 上一篇
master-slave1 master-slave1
转载至: MySQL主从复制及配置实现一 什么是MySQL主从复制MySQL主从复制是其最重要的功能之一,主从复制是指一台服务器充当主数据库服务器,另一台或多台服务器充当从数据库服务器,主服务器中的数据自动复制到从服务器中。对于多级复制,数
2018-07-11
下一篇 
mysql-event mysql-event
Mysql定时任务&存储过程Mysql属于中小型数据库系统,它的事件调度器是在mysql5.1才开始引入的,事件调度器是在Mysql5.1中新增的另一个特色功能,可以作为定时任务调度器,取代部分原先只能用操作系统任务调度器(如lin
2018-05-19
  目录