曲径通幽论坛

 找回密码
 立即注册
搜索
查看: 3708|回复: 0
打印 上一主题 下一主题

SET NAMES 分析详解

[复制链接]

4918

主题

5880

帖子

3万

积分

GROAD

曲径通幽,安觅芳踪。

Rank: 6Rank: 6

积分
34397
跳转到指定楼层
楼主
发表于 2012-7-4 17:14:49 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
了解 SET NAMES 之前,需要先了解 3 个和字符集相关的变量:
character_set_client ,  character_set_connection 和 character_set_results
实际上,SET NAMES 命令正好是一次性将这 3 个值进行修改,下面会提到。现在查看它们的默认值,可以通过下面的命令:
[Plain Text] 纯文本查看 复制代码
show variables like "%char%";




这几个变量是和客户端与服务器之间的连接相关的。那么什么是一个连接?
当客户端向服务器发送 SQL 语句时,比如查询,那么就会连接到服务器,然后服务器通过连接发送响应给客户端,如查询的结果集。
对于客户端连接,这样会导致一些关于连接的字符集和 校对规则的问题,这些问题均能够通过系统变量来解决,而这些变量就是上面提到的 3 个变量。


这里有两个概念:一是“字符集”,二是“
校对规则
” 。


字符集是众所周知的,比如西欧用的 latin1,简体中文的 GB2312, GBK 等,如下所示:




还可以用 SHOW CHARACTER SET 命令列出当前可用的字符集。


那什么是“校验规则” 呢?
校对规则就是一套对编码比较的规则。最简单的校对规则就是二元(binary)校对规则,即逐字节相等,比如上图中的 gb2312_bin , gbk_bin ,latin1_bin 。


任何一个给定的字符集至少会有一个校对规则,也可能有几个,可以使用 SHOW COLLATION 命令列出一个字符集的校对规则,比如:


上面的 SHOW COLLATION LIKE 'latin1%'; 命令那些名字以 latin1 开头的校对规则。比如
latin1_swedish_ci
代表的是 “瑞典/芬兰” ,“
latin1_danish_ci
” 代表的是 "丹麦/挪威" 。可以简单的着么认为:在一个字符集中,应用一个校对规则,可以相应的识别出一个或几个国家所使用的语言。


“校对规则“ 有以下 2 个特征:
(1) 两个不同的字符集不能有相同的校对规则。
(2) 每一个默认的字符集有一个默认的校对规则,例如 latin1 默认的校对规则是 latin1_swedish_ci 。


”校对规则“ 的命名约定为: _bin 表示”二元“, _ci 表示大小写不敏感,_cs 表示大小写敏感。


现在回过来看一下上面所提到的 3 个变量。


character_set_client 变量是指
当客户端发出查询后,
服务器使用它来
作为客户端发送的查询中使用的字符集。比如上面使用的是 utf8 。 在服务器收到查询后,服务器会把
character_set_client
字符集转换到
character_set_connection 所指定的字符集。在转换完毕后,服务器会发送结果集或错误信息到客户端,那么在此之前需要转换为那种字符集呢?是的,就是
character_set_results 这个变量所指定的。


下面做一下实验。


先假设上述的几个变量分别使用的是 utf8 字符集。


接下来要查询一个表,表中的某个字段里有中文,查询语句如下:
[Plain Text] 纯文本查看 复制代码
select topic_title from phpbb3.phpbb_topics;



可以正常显示中文。


现在运行 SET NAMES 语句改变上面 3 个值:
[Plain Text] 纯文本查看 复制代码
SET NAMES GBK;
show variables like "%char%";




由上图可见,上面提到的 3 个变量的值都从原来的 utf8 改变为 gbk 了。接下来再像上面的同样查询一次:

[Plain Text] 纯文本查看 复制代码
select topic_title from phpbb3.phpbb_topics;



原来正常显示的中文乱码了。


为什么呢?
从头开始分析。首先对于运行 SQL 命令的终端就是”客户端“。假设现在 character_set_client 是 GBK ,那么客户端就会要求使用 GBK 去查询,那么服务器收到查询请求后(建立连接后),它会先去查看 character_set_connection 的值,并将 character_set_client 的值转换成它的字符集,查询完毕后,根据 character_set_results 所设置的字符集返回。实际上,上面的乱码和 character_set_client 和 character_set_connection 都没有关系,问题是出在 character_set_results 上。因为对于 Linux 系统本身,它是使用 utf8 编码的,也就是说可以在终端(shell) 上能看到正常的 utf8 编码的中文汉字,而不能看到 GB2312,GBK 这种编码的中文形式 -- 也就是说,在这种情况下你看到的就是乱码。


对此,可以将 character_set_results 这个变量设置为 utf8 ,来验证我们上面的所述:
[Plain Text] 纯文本查看 复制代码
SET character_set_results = utf8;





再次和上面的同样查询,可以看到:


由上图可以看到,中文在终端下又恢复了显示。


下面,我们先将上面的 3 个值还原为 utf8 ,接着可以再做一个试验。这次我们将在 php 文件里做同样的事情。查询数据库的 PHP 代码如下:
[Plain Text] 纯文本查看 复制代码
<?php
        require_once("dbfuncs.php");


        $link = create_connection();


        $sql = "SELECT `topic_title` FROM `phpbb_topics` WHERE `topic_id` = 13";


        $result = execute_sql("phpbb3", $sql, $link);


        $row = mysql_fetch_assoc($result);


        echo $row["topic_title"];
?>

上面代码中的 dbfuncs.php 的代码为:
[Plain Text] 纯文本查看 复制代码
<?php
function create_connection()
{
    $link = mysql_connect("localhost", "root", "www.groad.net")
        or die("Can not connect to database<br><br>" . mysql_error());


    mysql_query("SET NAMES utf8");
    return $link;
}


function execute_sql($database, $sql, $link)
{
    $db_selected = mysql_select_db($database, $link)
        or die("Select database failed<br><br>" . mysql_error($link));


    $result = mysql_query($sql, $link);


    return $result;
}
?>

打开该网页后发现乱码了:


道理很简单,返回是按照 utf8 的编码返回的,如果你想看到正常的结果显示,那么可以在浏览器里选择 UTF8 编码。这是因为,在简体中文的系统下,浏览器若是从 http header 中无法知道 content-type 里所指出的编码时,默认使用的是 GB2312 或 GBK 编码来显示网页。所以,你如果叫客户手动去选择浏览器的”编码“来正常显示你的网页这是一种多么不友好的一种做法。假如你确定浏览你改网页的用户一定是简体中文用户,那么可以在上面的 PHP 代码中添加一句:
[Plain Text] 纯文本查看 复制代码
mysql_query("SET NAMES GBK");

这样做之后,简体中文用户显示结果就是正常的了。当然,即使不用上面的那条语句,那么可以使用header()  函数来通知浏览器使用 UTF8 编码来显示网页,比如:
[Plain Text] 纯文本查看 复制代码
header(”Content-type: text/html;charset=utf-8″);



注意,上面的那条语句,不会改变服务器里所里设置的变量。



本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?立即注册

x
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Archiver|曲径通幽 ( 琼ICP备11001422号-1|公安备案:46900502000207 )

GMT+8, 2025-6-18 23:39 , Processed in 0.065019 second(s), 24 queries .

Powered by Discuz! X3.2

© 2001-2013 Comsenz Inc.

快速回复 返回顶部 返回列表