28、MySQL字符集参数详解

前言:

需要明确的地方

  • 问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之间互相转换,不用过分考虑大转小丢失信息的问题)

你可能感兴趣的:(28、MySQL字符集参数详解)