mysql字符乱码详解

来源:互联网 发布:万网备案是阿里云吗 编辑:程序博客网 时间:2024/06/07 07:06

  在处理mysql中文时,我们常常遇到字符编码的问题,本来主要讲解其乱码产生的原因,并给出解决方案。

  字符原理

    Latin1是ISO-8859-1的别名是单字节编码,向下兼容ASCII,其编码范围是0x00-0xFF,0x00-0x7F之间完全和ASCII一致,0x80-0x9F之间是控制字符,0xA0-0xFF之间是文字符号


     GBK总体编码范围为 8140-FEFE,首字节在 81-FE 之间,尾字节在 40-FE 之间,剔除 xx7F 一条线。


     UTF8使用一至四字节编码,0x00–0x7F范围内是一位,和ASCII保持一致。其它字符用二至四个字节变长表示。

    提示

     1、0x00-0x7F区间,上述字符集是一致的,也就是说英文字符无需转码。

     2、不同编码,字符集合不完全一样,存在某字符集的字符无法映射到另外一个字符集。

          比如gbk编码中的中文字符,转成latin-1编码时,就找不到对应的二进制编码。Mysql做字符集转换的时候,gbk中文字符-               >latin-1,很多就转成'?'号(0x3f),这种大集合转成小集合,基本是不可逆的

     实验

  •     首先用show variables like ‘character%’ 查看mysql支持的中文编码,结果如下
           
  •    创建三个表test_gbk,test_utf8,test_latin1,创建的时候字符集分别为gbk,utf8,latin1。表结构为
           

          创建的sql语句

           create table test_gbk (

                         name varchar(100) not null

            )engine = myisam character set gbk;    

  • 问题1

    -----------------------------------

    执行下列语句: 

    set names 'latin1'; 

    insert into test_latin1 values( '中'); //此处'中'为gbk格式

    select name from test_latin1;

    结果是乱码,还是正常显示?

     

    问题2

    -------------------------------------

    执行下列语句:

    set names 'gbk'; 

    insert into test_latin1 values( '中'); //此处'中'为gbk格式

    select name from test_latin1;

    结果是乱码,还是正常显示?

     

    问题3

    -------------------------------------

    执行下列语句:

    set names 'latin1'; 

    insert into test_utf8 values( '中'); //此处'中'为gbk格式

    select name from test_utf8;

    结果是乱码,还是正常显示?  

  • mysql执行过程

        对一个mysql的执行过程,字符集转换,一般涉及到一下三个步骤:

        1、收到请求,将请求数据从 character_set_client -> character_set_connection。

        2、内部操作,将数据从character_set_connection -> 表创建的字符集。

        3、结果输出,将数据从表创建的字符集 -> character_set_results。 

        当执行set names "charset"; 相当于把        

         character_set_client, character_set_connection,character_set_results 统一设置为"charset"。

 

  • 终端显示字符集

        此外如果你用securecrt终端来显示的话,如果不想乱码的话,appearance->character encoding也需要设置           成正确的字符集。

------------------------------------------------问题详解----------------------------------------------------------

问题1

-----------------------------------

执行下列语句: 

set names 'latin1'; 

insert into test_latin1 values( '中'); //此处'中'为gbk格式

select name from test_latin1;

结果是乱码,还是正常显示?

答:结果是正常显示。

 

执行流程如下:

1、set names 'latin1';相当于把character_set_client, character_set_connection,character_set_results 统一设置为'latin1'。

2、Character_set_client告诉mysql server,传入的是一个latin1编码的,也就是单字节流,'中'这个输入,其实当作了0xD6 D0传入。

3、因为character_set_client -> character_set_connection -> table charset -> character_set_results为 latin1 -> latin1 -> latin1 -> latin1, 编码完全一致,数据没有做任何转换,所以输入是0xD6 D0,最后的输出也还原为0xD6 D0。

4、如果你的securecrt的显示字符集设置为gbk,那么最后的输出0xD6 D0就会显示成'中'。

 

问题2

-------------------------------------

执行下列语句:

set names 'gbk'; 

insert into test_latin1 values( '中'); //此处'中'为gbk格式

select name from test_latin1;

结果是乱码,还是正常显示?

答:结果是乱码。

 

执行流程如下:

1、set names 'gbk';相当于把character_set_client, character_set_connection,character_set_results 统一设置为'gbk'。

2、Character_set_client告诉mysql server,传入的是一个gbk编码的,'中'这个输入,当作了0xD6 D0传入。

3、因为character_set_client -> character_set_connection -> table charset -> character_set_results为 gbk-> gbk-> latin1 -> gbk, 其中gbk-> latin1的时候,因为'中'这个字符在latin1字符集里找不到,就会转换成'?'号(0x3F),然后latin1->gbk,'?'号在gbk字符集里面也是0x3F,最后输出就是0x3F,即'?'号。

 

问题3

-------------------------------------

执行下列语句:

set names 'latin1'; 

insert into test_utf8 values( '中'); //此处'中'为gbk格式

select name from test_utf8;

结果是乱码,还是正常显示?

答:正常显示。

 

执行流程如下:

1、set names 'latin1';相当于把character_set_client, character_set_connection,character_set_results 统一设置为'latin1'。

2、Character_set_client告诉mysql server,传入的是一个latin1编码的,'中'这个输入,当作了0xD6 D0传入。

3、因为character_set_client -> character_set_connection -> table charset -> character_set_results为 latin1-> latin1-> utf8 -> latin1, 其中latin1-> utf8的时候,输入'中' (0xD6 D0)会当作两个字符进行utf8转换,转换为0xC3 96 C3 90,然后utf8->latin1的时候,会把0xC3 96转换成0xD6, 0xC3 90转成0x D0,最后输出0xD6 D0。负负得正,之所以数据没有失真的原因是因为小集合往大集合转,再转回来,操作可逆。

4、如果你的securecrt的显示字符集设置为gbk,那么最后的输出0xD6 D0就会显示成'中'。

 

--------------------------------------------------终极解决方案----------------------------------------------------

从上面的问题执行流程来看,有没有终极解决方案呢?其实很简单,表创建的字符集和set names都设置成同一个字符集,就基本可以满足输入数据不会在转换过程中失真,也就是说输入是什么,输出就是什么。建议有中文的都设置成utf8字符集,一劳永逸。

            
0 0
原创粉丝点击