python --- 字符编码学习小结(一)
上半年的KPI,是用python做一个测试桩系统,现在系统框架基本也差不多定下来了。里面有用到新学的工厂设计模式以及以及常用的大牛写框架的业务逻辑和python小技巧。发现之前自己写的代码还是面向过程思想的多,基本没有面向对象的思想,近半年看的代码给了很大的触动,我需要升级我的技能了,于是也花了挺多时间在这个KPI学习上,现在先总结下在做这个系统时我所面临到的python的字符编码问题。
字符编码问题,如果处理有问题,可能直接就报错了;如果处理不得当,中文就会显示乱码。这是最初接触字符编码遇到问题最简洁的表达。一般都会遇到以下几个问题(逐渐升级的头疼问题):
1. 使用编辑器或者Python IDE直接打印中文,报错或者乱码;
2. 前台传输过来包含中文的字符串在后台打印,报错或者乱码;
3. DB交互,从DB查询或者insert的中文,操作时报错或者乱码;
4. 文件操作,从文件中读取或者写文件时,报错或者乱码;
除了第四个问题,暂时我没遇到过,前面3个问题,我遇到好几次了;遇到字符编码问题,如果想着就解决当前的问题,随便在百度上,狂搜各种方法乱试,可能还真能解决这个问题,但是耗时太长了,下次再遇到一样会头炸开,我觉得学习解决这个问题需要经过以下几个过程:
1. 简单的了解计算机字符编码的发展史,ASCII编码是啥? EASCII是啥?GBK是啥?unicode是啥?UTF-8是啥?UTF-16是啥?如果这些最初级的基本概念不了解,后面学习会很困难。
2. 理解python的字符串的数据类型,str和unicode,两者之间是如何转换的?
3. mysql支持哪些字符?mysql的环境变量跟字符集相关的有七,八个,都是神马意思?最简单的要怎么使用?
第一:字符编码的前世今生
1989年,荷兰人Guido van Rossum发明python语言,第一个公开发行版发行于1991年,当时在那个时代,是不关心编码问题的而且英文字符个数本身也是有限的,26个字母,10个数字,标点符号,键盘上加起来能输入的字符就一百多个,用一个字节来存储已经够了,8个比特位能存256个字符。于是美国人制定了一套字符编码标准ASCII。最开始的ASCII只定义了128个字符,包括96个字符和32个控制符,因此 ASCII 只使用了一个字节的后7位,最高位都为0。
随着时代的进步,计算机开始普及到千家万户,计算机进入中国面临的一个问题就是字符编码,中国的汉字是人类使用频率最多的文字,常见的汉字就有成千上万,大大超出了 ASCII 编码所能表示的字符范围了,于是中国人自己弄了一套编码叫 GB2312,GB2312 编码共收录了6763个汉字,同时他还兼容 ASCII,GB 2312的出现,基本满足了汉字的计算机处理需要,它所收录的汉字已经覆盖中国大陆99.75%的使用频率,不过 GB2312 还是不能100%满足中国汉字的需求,对一些罕见的字和繁体字 GB2312 没法处理,后来就在GB2312的基础上创建了一种叫 GBK 的编码,GBK 不仅收录了27484个汉字,同时还收录了藏文、蒙文、维吾尔文等主要的少数民族文字。同样 GBK 也是兼容 ASCII 编码的,对于英文字符用1个字节来表示,汉字用两个字节来标识。
世界语言种类有多少,计算机的字符编码相应就会增加多少。于是统一联盟国际组织提出了Unicode编码,Unicode的学名是”Universal Multiple-Octet Coded Character Set”,简称为UCS。Unicode有两种格式:UCS-2和UCS-4。UCS-2就是用两个字节编码,一共16个比特位,这样理论上最多可以表示65536个字符,不过要表示全世界所有的字符显示65536个数字还远远不过,因为光汉字就有近10万个,因此Unicode4.0规范定义了一组附加的字符编码,UCS-4就是用4个字节(实际上只用了31位,最高位必须为0)。世界上任何一个字符都可以用一个Unicode编码来表示,一旦字符的Unicode编码确定下来后,就不会再改变了。但是Unicode有一定的局限性,一个Unicode字符在网络上传输或者最终存储起来的时候,并不见得每个字符都需要两个字节,比如一字符“A“,用一个字节就可以表示的字符,却使用两个字节,太浪费空间了。UTF-8(8-bit Unicode Transformation Format)就出现了,UTF-8是一种针对Unicode的可变长度字符编码,又称万国码。UTF-8用1到6个字节编码Unicode字符。
第二:python 字符串类型
python2.X 系统的默认编码是ASCII,3.X系统就是unicode,所以在2.X系列遇到的编码问题会更多。
1
2
3
4
5
|
>>> import sys
>>> sys.getdefaultencoding()
'ascii'
|
现在会遇到第一类问题,在python源代码文件中如果不显示地指定编码的话,将出现语法错误:
这个提示很明显,非ASCII码在源代码中出现了。单纯的出现这类问题,可以采用以下方法解决:
方法一:在 文件前指定编码格式:
#!/usr/bin/env python #coding:UTF-8
方法二:设置整个系统的字符编码才能解决问题:(结合方法一一起使用)
default_encoding = 'utf-8'
if sys.getdefaultencoding() != default_encoding:
reload(sys)
sys.setdefaultencoding(default_encoding)
方法一和方法二都尝试了还有问题,可能就是你的编辑器的显示问题了,我使用的是pycharm,在file-setting里可以这样的设置:
调试IDE编码和project编码后,终于能打印中文了:
之前,刚学习python的时候,总结的一段:
1. 不设置源文件编码格式,输入中文,后直接打印,会提示存在‘non-ascii’,编译不通过
2. 设置源文件编码格式为gbk,输入中文后,打印乱码
3. 设置源文件编码格式为gbk,输入中文s1 = u'测试'后,打印正常
4. 设置源文件编码格式为gbk,输入中文后,先将字符串解码decode或者unicode方法,后打印正常
5. 设置源文件编码格式为utf-8,输入中文后直接输出正常
6. 设置工具和工程的默认编码为gbk,输入中文后,打印正常。
从python2.0开始,就有一种新的数据类型 Unicode Strings,但是在python3的到来,这个概念已经被弱化了。python2.*的默认编码格式是ASCII码,而python3.*的默认编码格式已经换成了Unicode。在python2中和字符串相关的数据类型,分别是str、unicode两种,他们都是basestring的子类,可见str与unicode是两种不同类型的字符串对象。区分一个
变量是字符还是unicode,可以使用type方法:
>>> a='好'
>>> type(a)
<type 'str'>
>>> a
'\xe5\xa5\xbd'
>>> b=u'好'
>>> type(b)
<type 'unicode'>
>>> b
u'\u597d'
Python中str和unicode之间是如何转换的呢?这两种类型的字符串类型之间的转换就是靠这两个方法decode
和encode
。
这2个函数的具体使用,就不举例了。网上这类文章挺多的。这时就有可能遇到第二个问题,前台传入的中文在后台乱码,无法处理。一般出现这类问题,都是前后台编码格式不一致导致的;
方法一:统一前后台编码格式;
方法二:如果无法统一,那取数据的时候就需要进行转码处理,之前我遇到一个问题,前台传入的是GBK格式的中文,我是做后台处理的,后台全系统都是用utf8编码的,接收到GBK的http请求后,显示的中文是乱码的,导致解析那段GBK的xml都报异常。后面做了调整,再接收到前台的GBK字符串后,首先decode(gbk)再encode(utf8)就成功了。
第三:操作DB,需要了解的mysql字符集。
使用python对DB的操作,随时都有可能出现乱码。网上搜以下2个方法偶尔也能解决问题:
方法一:conn = MySQLdb.connect(self.host,self.username,self.password,self.database,charset='gbk')
方法二:
1、 客户端和服务器建立连接后,客户端以什么字符编码发送数据?
答:服务器以character_set_client系统变量被设置, conn = MySQLdb.connect(self.host,self.username,self.password,self.database,charset='gbk') 只是设置了客户端发生数据的字符编码格式
2、服务器在收到语句后将其翻译成什么字符集?
答:服务器使用character_set_connection和collation_connection系统变量。它将收到的字符集从character_set_client转到character_set_connection,然后再进行处理。