异想天开

What's the true meaning of light, Could you tell me why

编码问题

日期:2018-04-04 19:33:05
  
最后更新日期:2018-07-11 20:40:26
用python实现了一个CGI server,该CGI可以挂在nginx后端。插入数据库时汉字数据是乱码了。场景是这样的:
前端用一个页面显示一个form表单,提交到后端插入数据库

在整个链路过程中,数据会经过如下几步:
浏览器 -> nginx -> python cgi -> mysql db

数据库和表创建都指定了编码为utf8:
[code lang="cpp"]
CREATE DATABASE IF NOT EXISTS videotag DEFAULT CHARSET utf8 COLLATE utf8_general_ci;
CREATE TABLE demo(
)ENGINE=MyISAM DEFAULT CHARSET=utf8;
[/code]
python连接的时候,先用set names utf8,然后插入数据。
那么问题会出在哪呢?一步步来排查。

1.首先以为浏览器使用了gbk编码传值:
刚开始的时候,是将取得参数
s.decode("gbk").encode("utf8")
decode表示什么意思呢? decode是按照指定的编码方式,解析字符串。
[code lang="cpp"]
aaa = "测试".decode("utf8")
bbb = "测试".decode("utf8").encode("gbk")
print type(aaa)
print type(bbb)
[/code]
结果:
当decode调用后,返回的是一个unicode对象,可以再编码为其他编码方式。


2.确认页面上使用了,
[code lang="cpp"]
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
[/code]
那么提交到后端的编码即为utf8,通过后台tcpdump抓包,发现传到CGI的时候,GET方法的参数变成了类似%E6%B5%8B%E8%AF%953:
用了python的urllib.unquote_plus解析
[code lang="cpp"]
s = 'http://www.google.com/?q1=%E6%B5%8B%E8%AF%953'
print urllib.unquote_plus(s)
[/code]
输出为:
http://www.google.com/?q1=测试3
说明这个函数解析是没错的,那么查看解析后的类型,发现为str type。

3.去掉decode和encode, 因为客户端直接是用utf8传参的,发现了日志出现如下错误:
[code lang="cpp"]
UnicodeEncodeError: 'latin-1' codec can't encode characters in position 45-48: ordinal not in range(256)
[/code]
字面意思是如果按latin字符编码,已经越界了。python中一个str类型的字符串,调用了mysql,默认使用了latin方式解码,发现遇到了超过256。
也就是说到了mysql db的python库时, 需要转换为unicode,至少告知类型不是latin了,那么重新编码一次解决:
s.decode("utf8")
之所以开始一下没写,是以为会自动识别出utf8对象,发现在字节流时,需要显示指定。

后记:
今天又遇到编码问题,总结一套简单的方法:可以直接把文件的内容用ord读取出来打印查看,比对差别。
[code lang="cpp"]
print ["%02x" % ord(c) for c in "人物_女"]
[/code]