MySQL 4.1 字符集支撑的原理

来源:网海拾贝  




下面要写的是一篇非常无聊的器械,充溢了大批百般百般的编码、转换、客户端、效劳器端、连接……呃,我本身都不甘愿宁可去看它,但想一想,写上去还是有点意义的,原因有四:

MySQL 4.1 对多言语的支撑有了很大变化 (这招致告终果的呈现); 
虽然大部分的处所 (包罗小我公家使用和主机供给商),MySQL 3 依然占主导职位;但 MySQL 4.1 是 MySQL 官方举荐的数据库,曾经有主机供给商结尾供给并将会越来越多; 
良多 PHP 法度圭表标准以 MySQL 作为默许的数据库操持软件,但它们一样往常不区分 MySQL 4.1 与 4.1 以下版本的区别,笼统地称“MySQL 3.xx.xx 以上版本”就知足安置需求了; 
因为 latin1 在良多处所 (下边会详尽描绘具体是哪些处所) 作为默许的字符集,成功的蒙蔽了良多 PHP 法度圭表标准的启示者和用户,掩饰笼罩了在中文等言语情况下会呈现的成绩; 
俭朴的说,MySQL 自身的变化和使用 MySQL 的 PHP 法度圭表标准对此纰漏,招致告终果的呈现和庞大化,而因为大部分用户使用的是英文,使这种成绩不被珍视。这里提到的 PHP 法度圭表标准,主要就 WordPress 而言。 

MySQL 4.1 字符集支撑的原理
MySQL 4.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 无从得知 (它最多只能猜测),所以 MySQL 4.1 要求客户端必须指定这个字符集,也便是 character_set_client,MySQL 的怪异之处在于,失掉的这个字符集并不急速转换为存储在数据库中的那个字符集,而是先转换为 character_set_connection 变量指定的一个字符集;这个 connection 层现实有什么用我不大明确,但转换为 character_set_connection 的这个字符集之后,还要转换为数据库默许的字符集,也便是说要颠末两次转换;当这个数据被输入时,又要由数据库默许的字符集转换为 character_set_results 指定的字符集。

一个类型的情况
类型的情况以我本身的电脑上安置的 MySQL 4.1 为例,我本身的电脑上安置着 Apache 2,PHP 5 和 WordPress 1.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 假定使用 MySQL 4.1 数据库,把编码改用 GB2312 就正常了,痛惜,这种正常只是貌似正常。

如那儿那边置惩成绩
假定你曾经不耐心了 (几乎是一定的),google 一下,会创造绝大部分的解答是,query 之前先实施一下:SET NAMES 'utf8',没错,这是处置惩方案,但本文的方针是申明');,这为什么是处置惩方案。

要包管成绩精确,必须包管数据表给与的花腔是精确的,也便是说,至少能够寄放齐备的汉字,那么我们只要两种选择,gbk 或许 utf-8,下面联系 utf-8 的景象。

因为设置文件设置的 default_character_set 是 utf8,数据表默许给与的便是 utf-8 设立树立的。这也应该是齐备给与 MySQL 4.1 的主机供给商应该给与的设置。所以我们要包管的只是客户端与 MySQL 交互之间指定编码的精确。

这只要两种能够,客户端以 gb2312 花腔发送数据,或许以 utf-8 花腔发送数据。

假定以 gb2312 花腔发送:

SET character_set_client='gb2312'
SET character_set_connection='utf8' 或许 
SET character_set_connection='gb2312'
都是可以的,都能够包管数据在编码转换中不呈现丧丢失,也便是包管管储入数据库的是精确的内容。

如何包管取出的是精确的内容呢?思考到绝大部分客户端 (包罗 WP),发送数据的编码也便是它所盼望收到数据的编码,所以:

SET character_set_results='gb2312'
可以包管取出给浏览器浮现的花腔便是 gb2312。

假定是第二种景象,客户端以 utf-8 花腔发送 (WP 的默许景象),可以给与下述设置:

SET character_set_client='utf8'
SET character_set_connection='utf8'
SET character_set_results='utf8'
这个设置就等价于 SET NAMES 'utf8'。

WP 应该作什么删改
还是那句话,客户端要发给数据库什么编码的数据,数据库是不成能切实知道的,只能让客户端本身申明');白,所以,WP 是必须发送精确的 SET... 给 MySQL 的。如何发送最相宜呢?台湾的 pLog 同仁给出了一些发起:

首先,测试效劳器能否 >= 4.1,编译时能否参与了 UTF-8 支撑;是则持续 
然后测试数据库以什么花腔存储 ( $dbEncoding); 
SET NAMES  $dbEncoding 
关于第二点,WP 的景象是差其它,依据下面的类型设置,只需用 WP,一定数据库是用 UTF-8 存储的,所以要依据用户设置的以 GB2312 还是 UTF-8 浏览来判定 (bloginfo('charset')),但这个值是要连接数据库以后才华失掉的,所以遵从最高的方法是连接数据库之后,依据这个设置设置一次 SET NAMES,而不用每次查询之前都设置一遍。

我的删改方法是多么的,在 wp_includes/wp-db.php 中添加: 

function set_charset( $charset)
{
    // check mysql version first.
     $serverVersion = mysql_get_server_info( $this->dbh); 
     $version = explode('.',  $serverVersion); 
    if ( $version[0] < 4) return; 

    // check if utf8 support was compiled in 
     $result = mysql_query("SHOW CHARACTER SET like 'utf8'", 
                           $this->dbh); 
    if (mysql_num_rows( $result) < = 0) return;

    if ( $charset == 'utf-8' ||  $charset == 'UTF-8')
         $charset = 'utf8';
    @mysql_query("SET NAMES ' $charset'",  $this->dbh); 
}
在 wp-settings.php 的 require (ABSPATH . WPINC . '/vars.php'); 后添加: 

 $wpdb->set_charset(get_bloginfo('charset'));
Posted in : Server-Side Author : jjgod

摘于:  http://jjgod.3322.org/2005/07/31/a-mysql-41-story/




版权声明: 原创作品,答允转载,转载时请务必以超链接方法标明文章 原始理由 、作者信息和本声明。不然将清查法律责任。

posted @ 2011-03-07 20:03  蓝色的天空III  阅读(121)  评论(0编辑  收藏  举报