编码
在了解python的编码之前,先了解一下目前计算机是如何处理字符。
首先,在计算机中会存储每个具体字符的形状,然后计算机可以根据下标来检索这些字符,之后再在显示器上打印出来。
上面说的字符存储实际上是字符集,即代表一堆字符的集合,下标可以认为是字符在字符集中排列的序号,即字符在字符集中的位置是固定的。了解完这个之后再来看各种字符集就明白了:
-
ASCII:最早的字符集,最初的ASCII字符集只包含了英文、数字和其他字符等128个字符。每个字符的在ASCII字符集中的位置代表它的索引下标。使用C语言的时候,在使用打印方法的时候是可以直接打印出下标数字所对应的字符的。
-
GBK、GB2312等其他基于ASCII的字符集:随着计算机的发展,各国都需要在计算机打印自己的语言字符了,因此各国基于ASCII之外又扩展了字符集,也就是在ASCII之外的编号全都用上了,还扩展到了8字节之外。由于各种制定字符集的时候只从本国语言出发而没有考虑其他语言,所以实际上这些字符集都是相互冲突的,也就是同篇文章或者应用中不能出现多种语言的情况,除非在原来的字符集上扩展,十分的不灵活。所以,计算机处理不同国家语言就需要下载各个国家的字符集。
-
Unicode:为了统一处理各种语言的字符集,unicode横空出世,正如他的名字(通用多八位编码字符集)一样,通用是unicode的使命。虽然unicode中的字符在字符集中有唯一的识别码(code point),但是unicode也衍生了其他的编码方式,比如现在常用的UTF-8,支持可变长度编码,使得一些原始的ASCII编码可以用单字节表示,以此来节省一些传输带宽。除此之外还有UTF-16、UTF-32等其他编码方式。这些衍生的编码可以转化成unicode码,从而定位到字符。
Python中的字符串编码处理
字符串在Python内部的表示是unicode编码,Python中字符串形态分成2种:一种是byte string(即编码后的字节),一种是unicode string
一般来说设置#coding=utf-8
后,所有带中文的参数都会是utf-8编码的 byte string。但是如果字符串前面加了u
前缀或者某些经过函数处理返回的字符串则是unicode string。
byte string和unicode string混用有时会抛出异常,例如:
>>> self.response.out.write("你好"+self.request.get("argu"))
UnicodeDecodeError: 'ascii' codec can't decode byte 0xe4 in position 0: ordinal not in range(128)
上面的例子中”你好”是utf-8编码的byte string,而self.request.get()返回的字符串则是unicode string。Python会自动把前面的”你好“转成unicode string,而Python默认的解码器是ascii,所以就会抛出解码异常。
解决办法有3种:
- 全部转成byte string
self.response.out.write("你好"+self.request.get("argu").encode('utf-8'))
- 全部转成unicode string
self.response.out.write(u"你好"+self.request.get("argu"))
- 更改默认解码器为utf-8
import sys
sys.setdefaultencoding('utf-8')
python的encode和decode
在python中,使用unicode类型作为编码的基础类型。即
decode encode
str ---------> unicode --------->str
字符串在Python内部的表示是unicode编码,因此,在做编码转换时,通常需要以unicode作为中间编码,即先将其他编码的字符串解码(decode)成unicode,再从unicode编码(encode)成另一种编码。
-
decode的作用是将其他编码的字符串转换成unicode编码,如str1.decode(‘gb2312’),表示将gb2312编码的字符串str1转换成unicode编码。
-
encode的作用是将unicode编码转换成其他编码的字符串,如str2.encode(‘gb2312’),表示将unicode编码的字符串str2转换成gb2312编码。
例子:
def get_unicode():
s = u"世界"
return s
print "你好" + get_unicode().encode("utf-8")
python2与python3的字符串
由于Python创始人在开发初期认知的局限性,其并未预料到python能发展成一个全球流行的语言,导致其开发初期并没有把支持全球各国语言当做重要的事情来做,所以就轻佻的把ASCII当做了默认编码。当后来大家对支持汉字、日文、法语等语言的呼声越来越高时,Python于是准备引入unicode,但若直接把默认编码改成unicode的话是不现实的, 因为很多软件就是基于之前的默认编码ASCII开发的,编码一换,那些软件的编码就都乱了。所以Python 2 就直接 搞了一个新的字符类型,就叫unicode类型,比如你想让你的中文在全球所有电脑上正常显示,在内存里就得把字符串存成unicode类型
时间来到2008年,python发展已近20年,创始人龟叔越来越觉得python里的好多东西已发展的不像他的初衷那样,开始变得臃肿、不简洁、且有些设计让人摸不到头脑,比如unicode 与str类型,str 与bytes类型的关系,这给很多python程序员造成了困扰。龟叔再也忍不了,像之前一样的修修补补已不能让Python变的更好,于是来了个大变革,Python3横空出世,不兼容python2,python3比python2做了非常多的改进,其中一个就是终于把字符串变成了unicode,文件默认编码变成了utf-8,这意味着,只要用python3,无论你的程序是以哪种编码开发的,都可以在全球各国电脑上正常显示,真是太棒啦!PY3 除了把字符串的编码改成了unicode, 还把str 和bytes 做了明确区分, str 就是unicode格式的字符, bytes就是单纯二进制啦。
最后一个问题,为什么在py3里,把unicode编码后,字符串就变成了bytes格式? 你直接给我直接打印成gbk的字符展示不好么?我想其实py3的设计真是煞费苦心,就是想通过这样的方式明确的告诉你,想在py3里看字符,必须得是unicode编码,其它编码一律按bytes格式展示。
编码建议
在Python中,当读取数据时,将数据解码成unicode,在内部统一使用unicode类型去处理。在向外传输数据时,则将unicode字符串编码成utf-8字节。