编码解码
一 什么是编码?
从明文到编码文本的转换称为“编码”,从编码文本又转回成明文则为“解码”。
//ASCII编码(一个字节)
256种不同状态,每种状态就唯一对应一个字符,每个电频称为一个比特位,约定8个比特位构成一个字节,这样计算机就可以用127个不同字节来存储英语的文字了。
//GB2312编码( ASCII 的中文扩展,两个字节)
规定一个小于127的字符的意义与原来相同,但两个大于127的字符连在一起时,就表示一个汉字,前面的一个字节(高字节)表示区号,后面一个字节(低字节)表示位号。
//GBK(对GB2312的扩展,两个字节) 和 GB18030编码(单字节、双字节和四字节)
只要第一个字节是大于127就固定表示这是一个汉字的开始,不管后面跟的是不是扩展字符集里的内容,GBK 包括了 GB2312 的所有内容。
//UNICODE编码(两个字节)
国际标谁化组织为了统一编码:提出了标准编码准则:UNICODE 。
UNICODE是用两个字节来表示为一个字符,它总共可以组合出65535不同的字符,覆盖世界上所有符号。
//utf8:(1~4个字节)
UTF-8是一种针对Unicode的可变长度字符编码,它可以使用1~4个字节表示一个符号,当字符在ASCII码的范围时,就用一个字节表示,兼容ASCII编码的。数据要保存到磁盘或者用于网络传输时,直接使用unicode就远不如utf8省空间。
Unicode与utf8的关系:
一言以蔽之:Unicode是内存编码表示方案(是规范),而UTF是如何保存和传输Unicode的方案(是实现)这也是UTF与Unicode的区别。
在计算机内存中,统一使用Unicode编码,当需要保存到硬盘或者需要传输的时候,就转换为UTF-8编码。
浏览网页的时候,服务器会把动态生成的Unicode内容转换为UTF-8再传输到浏览器:
Python的字符串
在最新的Python 3版本中,字符串是以Unicode编码的,也就是说,Python的字符串支持多语言,例如:
>>> print('包含中文的str') 包含中文的str
对于单个字符的编码,Python提供了ord()
函数获取字符对应的unicode编码的整数,chr()
函数把unicode编码转换为对应的字符:
>>> ord('A') 65 >>> ord('中') 20013 >>> chr(66) 'B' >>> chr(25991) '文'
如果知道字符的整数编码,还可以用十六进制这么写str
:
>>> '\u4e2d\u6587' '中文'
两种写法完全是等价的。
由于Python的字符串类型是str
,在内存中以Unicode表示,一个字符对应若干个字节。如果要在网络上传输,或者保存到磁盘上,就需要把str
变为以字节为单位的bytes
。
Python对bytes
类型的数据用带b
前缀的单引号或双引号表示:
x = b'ABC'
要注意区分'ABC'
和b'ABC'
,前者是str
,后者虽然内容显示得和前者一样,但bytes
的每个字符都只占用一个字节。
以Unicode表示的str
通过encode()
方法可以编码为指定的bytes
,例如:
>>> 'ABC'.encode('ascii') b'ABC' >>> '中文'.encode('utf-8') b'\xe4\xb8\xad\xe6\x96\x87' >>> '中文'.encode('ascii') Traceback (most recent call last): File "<stdin>", line 1, in <module> UnicodeEncodeError: 'ascii' codec can't encode characters in position 0-1: ordinal not in range(128)
纯英文的str
可以用ASCII
编码为bytes
,内容是一样的,含有中文的str
可以用UTF-8
编码为bytes
。含有中文的str
无法用ASCII
编码,因为中文编码的范围超过了ASCII
编码的范围,Python会报错。
在bytes
中,无法显示为ASCII字符的字节,用\x##
显示。
反过来,如果我们从网络或磁盘上读取了字节流,那么读到的数据就是bytes
。要把bytes
变为str
,就需要用decode()
方法:
>>> b'ABC'.decode('ascii') 'ABC' >>> b'\xe4\xb8\xad\xe6\x96\x87'.decode('utf-8') '中文'
如果bytes
中包含无法解码的字节,decode()
方法会报错:
>>> b'\xe4\xb8\xad\xff'.decode('utf-8') Traceback (most recent call last): ... UnicodeDecodeError: 'utf-8' codec can't decode byte 0xff in position 3: invalid start byte
如果bytes
中只有一小部分无效的字节,可以传入errors='ignore'
忽略错误的字节:
>>> b'\xe4\xb8\xad\xff'.decode('utf-8', errors='ignore') '中'
要计算str
包含多少个字符,可以用len()
函数:
>>> len('ABC') 3 >>> len('中文') 2
len()
函数计算的是str
的字符数,如果换成bytes
,len()
函数就计算字节数:
>>> len(b'ABC') 3 >>> len(b'\xe4\xb8\xad\xe6\x96\x87') 6 >>> len('中文'.encode('utf-8')) 6
可见,1个中文字符经过UTF-8编码后通常会占用3个字节,而1个英文字符只占用1个字节。
在操作字符串时,我们经常遇到str
和bytes
的互相转换。为了避免乱码问题,应当始终坚持使用UTF-8编码对str
和bytes
进行转换(str按utf-8解码为bytes,反之亦然)。
由于Python源代码也是一个文本文件,所以,当你的源代码中包含中文的时候,在保存源代码时,就需要务必指定保存为UTF-8编码。当Python解释器读取源代码时,为了让它按UTF-8编码读取,我们通常在文件开头写上这两行:
#!/usr/bin/env python # -*- coding: utf-8 -*-
第一行注释是为了告诉Linux/OS 系统,这是一个Python可执行程序,Windows系统会忽略这个注释;
第二行注释是为了告诉Python解释器,按照UTF-8编码读取源代码,否则,你在源代码中写的中文输出可能会有乱码。
申明了UTF-8编码并不意味着你的.py
文件就是UTF-8编码的,必须并且要确保文本编辑器正在使用UTF-8 without BOM编码。
二 py2的string编码
在py2中,有两种字符串类型:str类型和unicode类型;注意,这仅仅是两个名字,python定义的两个名字。
#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数据。
Python 2 悄悄掩盖掉了 byte 到 unicode 的转换,只要数据全部是 ASCII 的话,所有的转换都是正确的,一旦一个非 ASCII 字符偷偷进入你的程序,那么默认的解码将会失效,从而造成 UnicodeDecodeError 的错误。py2编码让程序在处理 ASCII 的时候更加简单。你复出的代价就是在处理非 ASCII 的时候将会失败。
三 py3的string编码
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 cmd下的乱码问题
#coding:utf8 print ('苑昊')
在pycharm运行,从文件往内存读,解释器都按utf-8解码,在内存是unicode编码,然后编译运行,pycharm终端能识别utf-8,所以都能正常运行。
在cmd中运行,从文件往内存读,python2解释器按utf-8解码,在内存中是unicode,然后编译运行,只是print的内容会传递给cmd.exe来显示,而在py2解释器里这个内容就是utf8编码的字节数据,可这个软件默认的编码解码方式是GBK,所以cmd.exe用GBK的解码方式去解码utf8自然会乱码。
在cmd中运行,从文件往内存读,python3解释器按utf-8解码,在内存中是unicode,然后编译运行,print的内容会传递给cmd.exe来显示,在py3解释器里这个内容就是utf8编码的unicode数据,所以cmd.exe用GBK的解码方式去解码unicode数据自然不会乱码。
五、各进制之间的相互转化
b = 22909 print('二进制bin:',bin(b))
print('八进制oct:',oct(b))
print('十六进制hex:', hex(b))
结果为:
二进制bin: 0b101100101111101 八进制oct: 054575 十六进制hex: 0x597d
int()函数将进制字符串转成相应的进制数
print(int('adfe43',16)) print(int('01010101',2))
结果为:
11402819 85
汉字的转为数字(十进制):
str = '好' print('汉字转十进制:', ord(str))
结果为:
汉字转十进制: 22909
数字转为汉字:(先转成十进制)
print ('十进制转换成汉字:',chr(22909)) 结果为: 好