设为首页 加入收藏

TOP

MySQL字符集编码(四)
2015-07-24 10:26:00 来源: 作者: 【 】 浏览:4
Tags:MySQL 字符集 编码
的原理来分析下整个编码转换过程:

  • 首先我们mysql客户端发送插入语句insert into test values("中文", "中文", "中文");,注意到"中文"的编码是跟我们的环境相关的,我这里是zh_CN.UTF-8,因此"中文"字节表示为\xE4\xB8\xAD\xE6\x96\x87.
  • 服务器端接收到该语句会当作utf8编码,因为character_set_client=utf8,接下来是会进行第一步转换,即将语句从character_set_client转成character_set_connection的编码,由于我们这里这2个编码相同,实际就不会转换(此外,如果插入的数据前面有latin1或者utf8等introducer标记,也不会转换,因为introducer标记已经指明了字面值字符的编码).
  • 接下来,数据要存储到数据库了,这个时候实际要插入的三个字段的编码都是原始编码\xE4\xB8\xAD\xE6\x96\x87,这个时候发生第二次编码转换,即由character_set_connection编码转换为数据表字段指定的编码.那么接下来,我们可以看到,由本身的UTF8编码与字段utf8相同,不需要进行转换.接下来看gbk字段,它的编码是gbk,这时会将原始编码s="\xE4\xB8\xAD\xE6\x96\x87"按照utf8编码转换为GBK编码,即执行s.decode('utf8').encode('gbk'),所以存储的是D6D0CEC4,也没有问题. 最后,看latin_utf8字段,同样需要转换编码,由于latin1表示不了utf8编码的范围,所以s.decode('utf8').encode('latin1')这个转换过程会出错,导致的结果就是latin_utf8字段存储的是??,即3F3F.
  • 最后就是select语句返回的结果分析,这是第三个需要转换编码的地方,即将字段从字段编码转换为character_set_results指定的编码.这也是我们上面为什么gbk字段和utf8字段都能正常显示中文的原因,因为在返回结果的时候,gbk字段会经过'\xD6\xD0\xCE\xC4'.decode('gbk').encode('utf8')返回,这样我们在utf8编码的mysql客户端能够正常显示gbk字段.同理,由于utf8字段本身与character_set_results,所以不会发生编码转换,原样返回\xE4\xB8\xAD\xE6\x96\x87,因此也是能正常显示的.而latin_utf8字段本身存储的就是3F3F,再经过编码转换,虽然utf8编码能够兼容latin1,但是本身的编码是3F3F,所以最终结果就是"??".

    4.2 解决方案

    这一小节就来说说4.1中的问题,根据上面的分析,为了表test中的latin_utf8字段能够正常的插入内容,我们不重新设置character_set_client和character_set_connection的情况下,那么有个好的方法就是加入introducer,关于introducer可以参见mysql官方文档.那么我们的插入语句改为

    mysql> insert into test values("中文", "中文", _latin1"中文");

    Query OK, 1 row affected (0.02 sec)

    由于指定了latin_utf8字段的introducer为_latin1,这样在第一次由character_set_client转换为character_set_connection的时候会忽略latin_utf8的转换,所以还是保持原来的utf8字符,接下来将其存入到latin1字段中,亦不会有问题,因为编码相同,不需要转换,所以latin_utf8字段实际存储的还是\xE4\xB8\xAD\xE6\x96\x87.这点可以通过下面的命令来验证:

    mysql> select hex(gbk), hex(utf8), hex(latin_utf8) from test;

    +----------+--------------+-----------------+

    | hex(gbk) | hex(utf8) | hex(latin_utf8) |

    +----------+--------------+-----------------+

    | D6D0CEC4 | E4B8ADE69687 | 3F3F |

    | D6D0CEC4 | E4B8ADE69687 | E4B8ADE69687 |

    +----------+--------------+-----------------+

    那么我们如果直接select查询,还会出错么呢?答案是会的,因为如前所说,查询的时候会将字段编码转换为character_set_results编码的,显然gbk和utf8字段都没有问题,但是对于latin_utf8字段,其值会通过s.decode('latin1').encode('gbk'),从而导致在查询的时候会乱码.

    mysql> select * from test;

    +--------+--------+----------------+

    | gbk | utf8 | latin_utf8 |

    +--------+--------+----------------+

    | 中文 | 中文 | ?? |

    | 中文 | 中文 | ??-?–? |

    +--------+--------+----------------+

    2 rows in set (0.01 sec)

    那么解决的方法也比较简单,就是中select语句中的字段前面加上binary标识,表示该字段查询结果不需要经过character_set_results的转换.如下:

    mysql> select gbk, utf8, binary latin_utf8 from test;

    +--------+--------+-------------------+

    | gbk | utf8 | binary latin_utf8 |

    +--------+--------+-------------------+

    | 中文 | 中文 | ?? |

    | 中文 | 中文 | 中文 |

    +--------+--------+-------------------+

    2 rows in set (0.00 sec)

    5.总结

    mysql编码系统复杂,依照原理和测试的结果来看,character_set_client一定要与传入的数据编码一致,不然就会容易出现乱码问题,character_set_connection可以与character_set_client不同,但是个人建议一样最好,免得出现其他问题.此外,如果对结果编码有要求,就设置下character_set_results编码,当然我个人觉得这三个编码一致是最省事的.此外,数据表字段编码如果用latin1编码,对于like搜索会有一些问题,最好大家依照自己需求来设定合理的字段编码了.

    我总结了这些地方,时间也很仓促,可能也有理解不到位的地方,还请大家指出.当然,最后要致谢凯哥,是凯哥最初的博客让我去研究了下mysql的编码,后续有新的认识我会再继续更新该文章.

    6.参考资料

    • mysql character set
    • 深入mysql字符集设置
    • MySQL-Sending-
首页 上一页 1 2 3 4 下一页 尾页 4/4/4
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
分享到: 
上一篇MySQL中数据表的查操作 下一篇Canrenametablebutcannottruncate..

评论

帐  号: 密码: (新用户注册)
验 证 码:
表  情:
内  容:

·bios设置按什么选择 (2025-12-26 17:20:08)
·知乎 - 知乎 (2025-12-26 17:20:04)
·http://和www.前缀网 (2025-12-26 17:20:01)
·Python爬虫教程(从 (2025-12-26 16:49:14)
·【全269集】B站最详 (2025-12-26 16:49:11)