背景: 刚做完一个django的数据查询web项目,数据来源于内部API查询,每次查询都需要调用若干API查询数据渲染在前端页面。由于,相关的数据不会经常变动,为了提高前端响应速度、在API不可用时依然能够查询,设计了缓存。API查询到的数据是json格式返回,缓存的数据是MySQL的Unicode编码,数据由此产生了两个来源:API和MySQL,导致了编码的错误。
1. 编码格式
1.1 ASCII
计算机只能处理0和1两种数字,为了让计算机能够处理文本信息(也就是文字字符串),就需要对这些信息进行编码。最早的计算机编码格式是ASCII码,采用8个bit表示一个字母,定义了128个字符,其中33个字符无法显示。8个bit位最多只能表示255个字符,只够英文国家使用,对全世界显然是不够的。
1.2 GB2312
为了满足计算机处理中文字符的需要,中国发布了GB2312编码规范,采用两个字节,16个bit表示一个图形字。收录6763个汉字,其中一级汉字3755个,二级汉字3008个;同时收录了包括拉丁字母、希腊字母、日文平假名及片假名字母、俄语西里尔字母在内的682个字符。
1.3 GBK
由于GB2312没有覆盖到全部汉字,一些古汉语字形和特殊的字符,GBK采用两个字节,16个bit表示一个图形字,同时完全兼容GB2312。GBK一共收录了21886个汉字和图形符号
1.4 Unicode
各个国家为了计算机能够处理本国语言,各自制定编码规范,不可避免会有各种各样的冲突。为了同一编码,出现了Unicode编码,采用多字节编码。Unicode的标准在不断发展,常见的是采用两个字节,16个bit表示一个字符的,如果是不常见的字符,也可以扩展到多个字节。
1.5 UTF-8
为了能表示更多的字符,Unicode采用长字节编码,这样会造成存储和带宽的浪费。UTF-8应运而生,采用边长的编码方式,实际上UFT-8是Unicode规则字库的一种实现形式。如果一个字节的首位是0,那么就表示一个字符,如果一个字节的首位是1,那么继续扩展一个字节编码判断。这种编码方式使UTF-8得到迅速推广。
1.6 各种编码的使用场景
ASCII:适用于英文的使用环境GB2312、GBK:适用于中文字符编码Unicode:通用,常作为中间编码,转换其他编码UTF-8:节约存储空间和宽带资源,应用非常广
2. Python的编码
Python默认脚本的编码方式是ASCII,如果使用了非ASCII的字符,通常会在第一行或第二行指定编码方式,# -*- coding=utf-8 -*-
或者 #coding=utf-8
,当然也可以采用其他字符集,gbk等,但强烈建议保持utf8。
2.1 字典类型
字典是可变容器模型,可存储任意类型对象。键必须是唯一的,但值不必。格式如下:
|
|
在ipython下执行,返回类型:
|
|
2.2 列表和元组
列表的数据项不需要具有相同的类型,列表是使用中括号中逗号分割单元定义的,列表的元素允许相同。格式如下:
|
|
在ipython下执行:
|
|
元组是使用圆括号中逗号分割单元定义,但是元组的内容是不允许改变的。格式如下:
|
|
在ipython下执行:
|
|
2.3 Python的字符数据类型
Python中有两种字符串类型,分别是 str 类型(8 bit的序列)和 unicode类型(每个unit是一个unicode对象)。当Python读取一个文本内容时,保持的对象为str类型。而以Unicode编码的字符串需要用u’‘表示。
|
|
上面编码使用的十六进制显示,使用print可以直接打印出表示的字符。可以看到字符一样,不同的编码存储开销是不一样的。
2.4 JSON数据
json字符串实际上是字符串,只不过它是由单引号包裹的,但是必须符合一定的字符规则。这里有四条:
- 并列的数据之间用逗号(", “)分隔
- 映射用冒号(” : “)表示
- 并列数据的集合(数组)用方括号(”[]")表示
- 映射的集合(对象)用大括号("{}")表示
json模块提供了两个函数 json.dumps() 和json.loads() 来编码和解码json数据。使用起来非常简单,需要注意的是dumps之后,tuple会变成list,loads之后不会完全恢复。json模块还提供dump和load函数,应用与需要将json存储到文件、套接字,而当做字符串处理时,使用dumps和loads函数。还有一点需要注意,loads会对字符串进行Unicode的编码,如果需要保持原编码,请使用ast.literal_eval。在python下执行,返回类型:
|
|
同时,Python还提供ord()和chr()两个函数对字母和数字相互转换。
3. MySQL的字符编码
3.1 基本概念
MySQL字符集包括字符集和校对规则两个概念。字符集定义MySQL存储字符串的方式,校对规则定义比较字符串的方式。字符集和校对规则是一对多的关系,MySQL支持30多种字符集的70多种校对规则。
3.2 校对规则选择
使用utf8_general_ci的速度快,而使用utf8_unicode_ci比较准确。 utf8_unicode_ci的准确主要体现德语和法语上,对于一般的国内应用场景,建议使用general规则就足够。另外,由于unicode和general对大小写都不敏感,utf8_bin_ci也有一定的应用场景。
3.3 注意事项
字符集通常直接选择utf8编码就足够。但是在有Emoji表情存储需求时会出现错误。MySQL的utf8编码最多3个字节,而Emoji表情是4个字节。为了能够存储Emoji,应该选用utf8mb4字符集。
4. 参考
- http://python3-cookbook.readthedocs.io/zh_CN/latest/c06/p02_read-write_json_data.html
- http://dev.mysql.com/doc/refman/5.7/en/charset-unicode-sets.html
- https://zh.wikipedia.org/wiki/GB_2312
- https://zh.wikipedia.org/zh-cn/Unicode