完美解决MySQL中文乱码问题
作者:bea
下面要写的是一篇非常无聊的东西,充斥了大量各式各样的编码、转换、客户端、服务器端、连接……呃,我自己都不愿意去看它,但想一想,写下来还是有点意义的,原因有四:
MySQL4.1对多语言的支持有了很大变化(这导致了问题的出现);
尽管大部分的地方(包括个人使用和主机提供商),MySQL3仍然占主导地位;但MySQL4.1是MySQL官方推荐的数据库,已经有主机提供商开始提供并将会越来越多;
许多PHP程序以MySQL作为默认的数据库管理软件,但它们一般不区分MySQL4.1与4.1以下版本的区别,笼统地称“MySQL3.xx.xx以上版本”就满足安装需求了;
因为latin1在许多地方(下边会详细描述具体是哪些地方)作为默认的字符集,成功的蒙蔽了许多PHP程序的开发者和用户,掩盖了在中文等语言环境下会出现的问题;
简单的说,MySQL自身的变化和使用MySQL的PHP程序对此忽略,导致了问题的出现和复杂化,而由于大部分用户使用的是英文,使这种问题不被重视。这里提到的PHP程序,主要就WordPress而言。
MySQL4.1字符集支持的原理MySQL4.1对于字符集的指定可以细化到一台机器上安装的MySQL,其中的一个数据库,其中的一张表,其中的一栏,应该用什么字符集。但是,传统的Web程序在创建数据库和数据表时并没有使用那么复杂的配置,它们用的是默认的配置,那么,默认的配置从何而来呢?
编译MySQL时,指定了一个默认的字符集,这个字符集是latin1;
安装MySQL时,可以在配置文件(my.ini)中指定一个默认的的字符集,如果没指定,这个值继承自编译时指定的;
启动mysqld时,可以在命令行参数中指定一个默认的的字符集,如果没指定,这个值继承自配置文件中的;
此时character_set_server被设定为这个默认的字符集;
当创建一个新的数据库时,除非明确指定,这个数据库的字符集被缺省设定为character_set_server;
当选定了一个数据库时,character_set_database被设定为这个数据库默认的字符集;
在这个数据库里创建一张表时,表默认的字符集被设定为character_set_database,也就是这个数据库默认的字符集;
当在表内设置一栏时,除非明确指定,否则此栏缺省的字符集就是表默认的字符集;
这个字符集就是数据库中实际存储数据采用的字符集,mysqldump出来的内容就是这个字符集下的;
简单的总结一下,如果什么地方都不修改,那么所有的数据库的所有表的所有栏位的都用latin1存储,不过我们如果安装MySQL,一般都会选择多语言支持,也就是说,安装程序会自动在配置文件中把default_character_set设置为UTF-8,这保证了缺省情况下,所有的数据库的所有表的所有栏位的都用UTF-8存储。
当一个PHP程序与MySQL建立连接后,这个程序发送给MySQL的数据采用的是什么字符集?MySQL无从得知(它最多只能猜测),所以MySQL4.1要求客户端必须指定这个字符集,也就是character_set_client,MySQL的怪异之处在于,得到的这个字符集并不立即转换为存储在数据库中的那个字符集,而是先转换为character_set_connection变量指定的一个字符集;这个connection层究竟有什么用我不大明白,但转换为character_set_connection的这个字符集之后,还要转换为数据库默认的字符集,也就是说要经过两次转换;当这个数据被输出时,又要由数据库默认的字符集转换为character_set_results指定的字符集。
一个典型的环境典型的环境以我自己的电脑上安装的MySQL4.1为例,我自己的电脑上安装着Apache2,PHP5和WordPress1.5.1.3,MySQL配置文件中指定了default_character_set为utf8。于是问题出现了:
WordPress按照默认情况安装,所以所有的表都用UTF-8存储数据;
WordPress默认采用的浏览字符集是UTF-8(Options->Reading中设置),因此所有WP页面的meta中会说明charset是utf-8;
所以浏览器会以utf-8方式显示所有的WP页面;这样一来Write的所有Post,和Comment都会以UTF-8格式从浏览器发送给Apache,再由Apache交给PHP;
所以WP从所有的表单中得到的数据都是utf-8编码的;WP不加转换的直接把这些数据发送给MySQL;
MySQL默认设置的character_set_client和character_set_connection都是latin1,此时怪异的事情发生了,实际上是utf-8格式的数据,被“当作latin1”转换成……居然还是转换成latin1,然后再由这个latin1转换成utf-8,这么两次转换,有一部分utf-8的字符就丢失了,变成??,最后输出的时候character_set_results默认是latin1,也就输出为奇怪的东西了。
最神奇的还不是这个,如果WordPress中设置以GB2312格式阅读,那么WP发送给MySQL的GB2312编码的数据,被“当作latin1”转换后,存进数据库的是一种奇怪的格式(真的是奇怪的格式,mysqldump出来就能发现,无论当作utf-8还是当作gb2312来读都是乱码),但如果这种格式以latin1输出出来,居然又能变回GB2312!
这会导致什么现象呢?WP如果使用MySQL4.1数据库,把编码改用GB2312就正常了,可惜,这种正常只是貌似正常。
如何解决问题如果你已经不耐烦了(几乎是肯定的),google一下,会发现绝大部分的解答是,query之前先执行一下:SETNAMES'utf8',没错,这是解决方案,但本文的目的是说明,这为什么是解决方案。
要保证结果正确,必须保证数据表采用的格式是正确的,也就是说,至少能够存放所有的汉字,那么我们只有两种选择,gbk或者utf-8,下面讨论utf-8的情况。
因为配置文件设置的default_character_set是utf8,数据表默认采用的就是utf-8建立的。这也应该是所有采用MySQL4.1的主机提供商应该采用的配置。所以我们要保证的只是客户端与MySQL交互之间指定编码的正确。
这只有两种可能,客户端以gb2312格式发送数据,或者以utf-8格式发送数据。
如果以gb2312格式发送:
SETcharacter_set_client='gb2312'
SETcharacter_set_connection='utf8'或者
SETcharacter_set_connection='gb2312'
都是可以的,都能够保证数据在编码转换中不出现丢失,也就是保证存储入数据库的是正确的内容。
怎么保证取出的是正确的内容呢?考虑到绝大部分客户端(包括WP),发送数据的编码也就是它所希望收到数据的编码,所以:
SETcharacter_set_results='gb2312'
可以保证取出给浏览器显示的格式就是gb2312。
如果是第二种情况,客户端以utf-8格式发送(WP的默认情况),可以采用下述配置:
SETcharacter_set_client='utf8'
SETcharacter_set_connection='utf8'
SETcharacter_set_results='utf8'
这个配置就等价于SETNAMES'utf8'。
WP应该作什么修改还是那句话,客户端要发给数据库什么编码的数据,数据库是不可能确切知道的,只能让客户端自己说明白,所以,WP是必须发送正确的SET...给MySQL的。怎么发送最合适呢?台湾的pLog同仁给出了一些建议:
首先,测试服务器是否>=4.1,编译时是否加入了UTF-8支持;是则继续
然后测试数据库以什么格式存储($dbEncoding);
SETNAMES$dbEncoding
对于第二点,WP的情况是不同的,按照上面的典型配置,只要用WP,肯定数据库是用UTF-8存储的,所以要根据用户设置的以GB2312还是UTF-8浏览来判断(bloginfo('charset')),但这个值是要连接数据库以后才能得到的,所以效率最高的方式是连接数据库之后,根据这个配置设置一次SETNAMES,而不必每次查询之前都设置一遍。
我的修改方式是这样的,在wp_includes/wp-db.php中增加:
functionset_charset($charset)
{
//checkmysqlversionfirst.
$serverVersion=mysql_get_server_info($this->dbh);
$version=explode('.',$serverVersion);
if($version[0]<4)return;
//checkifutf8supportwascompiledin
$result=mysql_query("SHOWCHARACTERSETlike'utf8'",
$this->dbh);
if(mysql_num_rows($result)<=0)return;
if($charset=='utf-8'||$charset=='UTF-8')
$charset='utf8';
@mysql_query("SETNAMES'$charset'",$this->dbh);
}
在wp-settings.php的require(ABSPATH.WPINC.'/vars.php');后增加:
$wpdb->set_charset(get_bloginfo('charset'));
有用 | 无用猜你喜欢
您可能感兴趣的文章:
- 针对谷歌如何增加有效的外部链接
- 如何提高新网站在百度的权重
- 如何查询网站流量和提高网站流量
- Google adsense申请和18个需要注意的事项
- 网站制作之怎样优化网站
- 影响百度快照更新的因素有哪些
- 浅谈百度知道推广技巧
- SEO优化策略在企业级网站中的应用
- 细说搜索引擎不收录网站页面
- SEO以及与SEO相关的各种营销公司
- httpd.ini和.htaccess的写法与配置规则
- 新建的网站生存之该如何推广
- MySQL启动不了,无法启动MySQL服务解决方法
- mysql数据库导入、备份和导出常用命令总结
- mysql当天日期函数使用小结
- sql查询优化:查询语句优化小结
- MySQL教程之error 150错误提示解决办法
- MySQL教程之六种强制修改Root密码方法
- 解决MySQL 1067错误方法