当一个Mysql连接请求从客户端传来的时候,服务器认为它的编码是character_set_client,
然后会根据character_set_connection把请求进行转码,从character_set_client转成character_set_connection,
然后更新到数据库的时候,再转化成字段所对应的编码
如果使用了set names指令,那么可以修改character_set_connection的值,
也同时会修改character_set_client和character_set_results的值
当从数据库查询数据返回结果的时候,将字段从默认的编码转成character_set_results
实例分析:
DB采用utf8存储数据,默认连接也使用utf8,客户端请求采用gb2312,调用了set names “gb2312”
当客户端gb2312的请求过来的时候,由于先前调用了set names,所以character_set_client、
character_set_connection、character_set_results都被改成了gb2312,
所以一路过来直到更新到db字段之前都是gb2312编码,然后因为db字段是utf8的,
所以会再进行一次转码,从gb2312转成utf8,最终以utf8格式存储在数据库中
当客户端要查询显示一个gb2312的内容时,也先set names “gb2312”,
这样会把character_set_results改成gb2312,当服务器查询出结果返回的时候就会从utf8转成gb2312,
适合页面显示的需求了
如果没有使用set names,那么从客户端过来的gb2312将一直被当成是character_set_client编码(比如说是utf8),
然后一路因为character_set_connection和character_set_client,所以不会进行转码,然后直到更新db字段都不会去转码,
所以就把请求中的内容直接更新为db字段的值了,就出现了乱码
所以要解决乱码问题的关键是要正确识别客户端过来的请求的编码,即character_set_client,同时因为要考虑到页面的显示,所以也要考虑到返回的结果的编码(character_set_results)必须和页面显示的编码一致,而character_set_connection貌似没有很大用处?
一点补充:
如果配置文件中[mysql]default-character-set=gb2312,那么效果和每次set names “gb2312”是一样的,
每个连接都会将character_set_client、character_set_connection、character_set_results都被设置为gb2312
------------------------------------------------------------------------------------------------------
我们传统意义上说的编码其实是指字符集,它包括两个方面,一个是存储的字符,另外一个是映射关系,也就是真正的编码。各种字符集的存储的字符都是差不多的,就那么几个字符,而编码却是各不相同,是真正发挥威力的地方。
原来的系统数据存储采用gbk字符集,因为版本原因,升级后系统必须采用latin1字符集来存储,所以新的数据库中存储的是gbk的字符,而使用的是latin1的编码。所以这种数据只能在需要显示gbk的页面上正确显示,在显示其他字符集的地方就会是乱码。
页面显示gbk的流程是:程序(或者mysql配置文件中)中设置了结果集采用latin1,mysql发现latin1就是当前字段的实际字符集,所以不进行任何转化,直接返回,但是这个时候实际上返回的是gbk的字符序列,当页面得到这个字符序列进行显示的时候,因为要显示gbk的数据,所以会采用gbk编码对得到的字符序列进行编码,得到实际的字符,而用gbk编码对gbk字符序列进行编码自然不会出错。
而如果要显示utf8编码,则页面显示的时候会采用utf8编码对gbk字符序列进行编码,自然得到的是乱码了。
由于有两个数据库之前采用的是gbk的字符集,要在utf8编码下显示就存在问题,所以必须对存储的数据进行转换。而mysql查询的过程中不是自己会根据设定的编码进行转换么?但是不幸的是,它自带的这个功能在我们这样的情况下没有任何用处。
Mysql编码转换中涉及到的三个变量我们上面提到过,是character_set_client、character_set_connection、character_set_results,character_set_client是插入数据的时候采用的字符集,character_set_connection是查询字符串采用的字符集,character_set_results是查询结果需要返回的字符集。这三个变量的使用应该需要有字符串和编码统一的前提,否则可能会出乱子,莫名其妙的乱码问题就会呈现在你面前。而我们的数据存储采用的字符和编码是不统一的,所以不能直接使用这三个变量来编码,所以只能在程序中将数据进行编码转换。因为我们最终要显示的是utf8数据,所以我们存储的必须是utf8的字符序列(原因和上面介绍的显示gbk数据的时候一样),而我们从老的数据库中查询得到的是gbk的字符串,编码是latin1的,要插入到新的数据库中需要经历这么几个转换,第一步,要根据character_set_connection进行转换,因为我们默认character_set_connection是latin1编码,和我们存储的编码一致,所以不需要进行任何转换,然后第二步是插入到数据库中,因为character_set_client也是latin1,所以也不需要进行任何转换,所以我们需要插入的字符串序列应该是和根据character_set_connection进行转换之前的字符串序列一致的,而我们最终需要的是utf8的字符串序列,所以在根据character_set_connection进行转换之前也必须是utf8的字符串序列,也就是说我们要在得到老的数据后,将gbk字符串序列转换成utf8字符串序列。Php自带的mb_convert_encoding函数应该能够完成这个艰巨的任务。