posts - 15,comments - 21,trackbacks - 0
      相信大家在开发后台的过程中都遇到过中文乱码的问题,今天我就来讲讲其中的原因。
      我这建了3张表,test_latin1,test_utf8,test_gbk,表结构如下
      +-------+----------+------+-----+---------+-------+
      | Field | Type     | Null | Key | Default | Extra |
      +-------+----------+------+-----+---------+-------+
      | name  | char(32) | YES  |     | NULL    |       |
      +-------+----------+------+-----+---------+-------+

      我的前端是gbk的编码
      执行下面的语句
      set names 'latin1'
      insert into test_latin1 set name='王';('王'字是GBK编码)
      select name from test_latin1;
      结果是否为乱码?

      执行下面的语句
      set names 'gbk'
      insert into test_latin1 set name='王';('王'字是GBK编码)
      select name from test_latin1;
      结果是否为乱码?

      执行下面的语句
      set names 'latin1'
      insert into test_utf8 set name='王';('王'字是GBK编码)
      select name from test_utf8 ;
      结果是否为乱码?

      我们举个例子,假设一个汉字的字符编码为0xFFFF,它在屏幕上能够正常显示,如果汉字存入数据库的时候和从数据库中取出的时候,编码一致,那么它肯定不是乱码。反过来,如果输出的时候是乱码,那么它肯定被转码了,至于为什么被转码了,我们得看看mysql里面做了什么(mysql难道会把无码片变成了有码片?)
      首先mysql里面有2个概念,一个叫character set,一个叫collation。我们先说说character set。字符集就是数字,英文字符,汉字等编码格式,我们常见的是utf8,gbk,gb2312。mysql里面比较复杂,有4个东西跟它有关,分别是character_set_client,character_set_connection,character_set_database,character_set_results。set names (latin1)其实就是character_set_client=latin1,character_set_connection=latin1,character_set_results=latin1,它的流程是character_set_client ==> character_set_connection ==> Table Character ==> character_set_results。
      我们按照上面的流程,来分析第一个问题。
      set names 'latin1'----执行了character_set_client=latin1,character_set_connection=latin1,character_set_results=latin1;
      insert into test_latin1 set name='王';这句话,mysql做了什么事呢?首先,character_set_client,它会把王字的编码当成latin1的编码传递给character_set_connection(此时不会转码),character_set_connection会把编码传递给Table Character,因为表本身是latin1,所以此时也不需要转码,select name from test_latin1;mysql会把test_latin1中的编码传递给前端,此时也不需要转码,所以,走个流程下来,我们输入的是什么编码,输出的还是相同的编码,因此,第一个问题的答案是不会是乱码。我画个流程图latin1==>latin1==>latin1==>latin1,没有转码的过程
      
      我们在来看第二个问题。
      set names 'test_gbk'----执行了character_set_client=gbk,character_set_connection=gbk,character_set_results=gbk;
      insert into test_latin1 set name='王';character_set_client,它会把王字的编码当成gbk的编码传递给character_set_connection(此时不会转码),character_set_connection会把编码传递给Table Character,因为表是lanti1的编码格式,这个过程的时候就会进行转码,但是latin1的字符集小于gbk的字符集,所以它会找不到对应字符的编码,此时会以?代替。select name from test_latin1,此时会从latin1转码成gbk,但是此时latin1已经是错误的数据了,所以得到的gbk编码也是错误的了。流程gbk==>gbk==>latin1==>gbk,其中gbk==>latin1出了问题,我们select出来的数据也就不可能是输入时候的数据了。因此,这个问题的答案是乱码。

      第三个。
      set names 'test_latin1'
      insert into test_utf8 set name='王';character_set_client,它会把王字的编码当成latin1的编码传递给character_set_connection(此时不会转码),character_set_connection会把编码传递给Table Character,此时表是utf8的格式,因此会进行转码,latin1==>utf8,因为utf8的字符集>latin1字符集,因此,转码正常。select name from test_utf8;会从utf8转码成latin1,此时可以转码成功,因此我们最终得到的和输入的时候是一致的,因此答案不是乱码。流程latin1==>latin1==>utf8==>latin1,从小的字符集到大的字符集再到小的字符集,转码是不会有问题的。
      屁话了这么多,无非想告诉大家一个万精油方法,表创建的字符集和set names都设置成同一个字符集,就基本可以满足输入数据不会在转换过程中失真,也就是说输入是什么,输出就是什么。建议有中文的都设置成utf8字符集,一劳永逸。
posted on 2012-11-26 19:56 梨树阳光 阅读(2497) 评论(2)  编辑 收藏 引用 所属分类: 数据库

FeedBack:
# re: mysql中文乱码解析
2012-11-27 09:18 | zuhd
最后一句话是亮点  回复  更多评论
  
# re: mysql中文乱码解析
2012-11-27 12:23 | cy
分析的非常透彻,受用!  回复  更多评论
  

只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理