前言:
需要明确的地方
问1:utf-8与gbk互相转换会出现乱码吗?
答:不会!!!问2:那么两个不同的编码是如何实现互相转化的呢?
答:
通过Unicode作为中间人
通过utf-8与Unicode的对应规则, 先找到Unicode中对应的编码
然后根据Unicode与GBK对应规则,转换成GBK对应的编码即可。问3:大转小会导致信息丢失吗?
答:一些情况下,是会的。
比如:
GBK转latin1。
latin1只有一个字节,只能最多也就存储两百多个不同的字符,但是GBK就有两万多个字,就算你用Unicode去作为中间人转化,latin1也存储不了那么多啊,就会造成信息丢失的问题了,而这种问题是不可修复的。
再比如:
utf-8转GBK
utf-8中汉字是3个字节,而GBK只是两个字节,那么utf-8通过Unicode转GBK,因为常用汉字也就3000个,再多也就20000个?而GBK就已经包含了这么多汉字了,因而可以通过Unicode找到一一对应的汉字,而不会出现覆盖导致信息丢失的情况的。
总结:
像上面,多个字节转到只有1个字节的,还是有很大可能造成信息丢失的,而utf-8与GBK转换,一般就不会了。
- 问3:既然这么转换不会生成乱码,那么乱码又是如何产生的呢?
答:1、编码与解码规则不一样:当编码与解码对应规则不一样的时候。比如客户端显示编码为GBK,结果返回到客户端的是utf-8格式的,那么显示就是乱码了。
2、没有中间人,直接“大转小”:比如让GBK直接转成latin1的,不通过Unicode,就会造成信息丢失,导致乱码。
下面的set names 语句,就是为了避免上述两种乱码情况,而产生的。
一、set names 语句
一般来说,我们建表时总是在最后面加上一句“charset = utf-8”,如下:
使得服务器端的表,存储数据时编码都用utf-8格式(因为utf-8包含很多字符,并且节省存储空间)。
在27、GB2312与UTF8编码详解中,我们也讲了出现乱码的两种可能,以及相对应的解决办法(第一种情况转变编码规则即可)。
问:在数据库的日常操作过程中,我们如果遇到了乱码的情况,应该怎么用sql语句解决呢?
答:一般,需要在建表前加入一条sql语句
set names gbk#(或者用utf-8)
怎么理解这句sql呢?
二、三个参数
2.1、连接器的必要性
如下,一般我们进行数据库操作时,客户端(也就是命令行)中用的是GBK的编码格式。
查看命令行属性:
比如,下面的命令行中,“指南针”三个字就是GBK格式的:
在客户端收集到的是GBK编码,而数据库最终想存成utf-8编码,上面也说了,如果直接传入到数据库中,因为编码与解码不对应,会导致乱码。
那么一定会有一个承上启下的“翻译”的“中间人”来保证不会出现乱码的情况。
2.2、传入数据:client与connection的参数编码指定
问:如果按照上面,client端指定是GBK格式,database中默认是utf-8,那么连接器应该是什么格式的呢?
答:要么是GBK,要么是utf-8。
2.2.1、情况1
如果有这种情况:
- 事先指定连接器内部暂时存储的编码是GBK格式的
- 客户端是GBK格式,服务器端是utf-8格式
- client编码格式与connection解码格式相同,不执行转换操作,
- 数据在connection中暂时存储成GBK格式
- connection编码为GBK,与服务器中不同,因此需要执行转换操作
- 于是,通过Unicode转换,就成了utf-8并且存储到服务器中了。
过程很正常,并没有发生上述问题3 中的情况。
2.2.2、情况二
- 事先指定连接器内部暂时存储的编码是utf-8格式的
- 客户端是GBK格式,服务器端是utf-8格式
类似情况一,GBK到utf-8,一样通过连接器执行转换操作,一直到最后存储到数据中。
2.3、传出数据:results的参数编码指定
2.3.1、数据传出
经过上面一节的操作,我们已经完成了数据的存入(假设不存在乱码情况)。
读取数据时,数据从服务器返回到连接器,经过转换以后,在传出到results端以前,我们要明确告诉连接器,results端的显示编码规范是什么才行。
2.3.2举个栗子
1斤===0.5kg==1.101磅。
一个商人到中国收购沙子到美国去卖 -- > 一个数据的存储与返回过程
- 中国人说,我们价格是按斤来算的
- 商人说,我收购你的沙子,但是我的价格按照千克算的-- > client到connection
- 收完以后,放到背包里-- > connection把转换好的数据传入到服务器中
- 运到美国以后,商人掏出沙子-- > 数据从服务器再传出到connection
- 美国人说,我们买,但是我们价格是按照磅来算的-- > 数据从connection返回到results
因此,上面做生意的过程,我们一定要明确每一步中是按照什么价格来算的,如果你按千克卖,他按照斤收,肯定是不合适的。
对应到数据库的字符集问题上,我们一定要明确每一步的字符集都是什么,client是什么,connection是什么,results是什么。这三个参数一定要明确。否则就会乱码了。
比如,明明results端的显示编码规范是latin1,而你告诉连接器results端的显示规范是GBK,那么连接器最终就会输出到GBK编码到results端,results端解读不了就会发生乱码。
注意,从服务器返回来的数据也是显示在命令行中的,因此此时的results显示编码应该与命令行的属性编码一致
2.3、三个参数
2.3.1、遵守规范才能不乱码
综上要想不乱码,有三个参数需要明确的指出:
client,connection,results。
只要让这三个参数之间的转换遵守规范,就不会发生乱码。
问:应该遵守什么样的规范呢?
答:- 1、编码与解码一致
- 2、不能导致字节丢失,编码之间的大小关系要考虑好。
2.3.2、一些例子
2.3.2.1、不乱码
比如,对于客户端是GBK,服务器存储UTF-8的格式
- 需要告诉服务器,客户端是GBK的
- 连接器使用utf-8暂时存储
- 最终返回值是GBK格式的
这样就不会乱码。如下:
2.3.2.2、乱码
a、编码与解码规则不一致的情况
例题1:
客户端传入的是GBK,但就是欺骗连接器,我传入的是utf-8格式的。那么连接器就按照utf-8的格式解码传入的数据:
可见设置(第一个红框)之后,转换出错(第二个红框):
例题2:
results端显示格式是GBK,但是非要连接器返回utf-8格式的。解码时GBK,返回的数据编码是utf-8,那么就会乱码。
b、大转小,导致信息丢失,产生乱码
例题3:
客户端设为GBK,连接器设置为latin1,服务器为utf-8。
此时,会因为GBK容量大于latin,在编码转换的时候,导致字节丢失。并且是不可修复的乱码。
因此,应该遵循的原则:
服务器 >= conntion >= client
2.4、set names gbk的意思
经过上面那么多的铺垫,我们应该会理解这句sql的意思了。
如果三个参数(客户端,连接器,返回值)都是GBK,就可以在sql语句中,简写成set names gbk。
三、在网页中的字符集参数设置
3.1、网页中编码与解码的统一
一般,网页中的编码格式与显示的格式都是utf-8的。
如下图,
首先,看右下角,说明此编辑器的编码规则是utf-8。
那么也就说明,此时编辑的文件里面的所有字符(这些英文字符与汉字)都是用utf-8格式的。与上面相对应,我们在解析网页的时候,要确定,解码规则是utf-8才行。
参见下图的第一个红框“charset = utf-8”
如果在第一个红框中,设置“charset=GBK”,那么因为编码与解码不同意,网页解码以后,显示出来的结果就是乱码了。
3.2、网页与数据库的交互
一般来说,网页是utf-8,服务器表中数据也是utf-8。那么在建立数据库时,也可以在数据库前用set names utf8这条来保证不会发生乱码的情况。
问:旧网页(client)中是GBK格式,旧服务器表中存储的是GBK格式,新网页(results)是utf-8格式,应该怎么设置参数呢?
传递信息的过程:
client传给服务器,服务器
答:同上。(utf-8与GBK之间互相转换,不用过分考虑大转小丢失信息的问题)