曲径通幽论坛
标题: SET NAMES 分析详解 [打印本页]
作者: beyes 时间: 2012-7-4 17:14
标题: SET NAMES 分析详解
了解 SET NAMES 之前,需要先了解 3 个和字符集相关的变量:
character_set_client , character_set_connection 和 character_set_results
实际上,SET NAMES 命令正好是一次性将这 3 个值进行修改,下面会提到。现在查看它们的默认值,可以通过下面的命令:
[Plain Text] 纯文本查看 复制代码
show variables like "%char%";
[attach]732[/attach]
这几个变量是和客户端与服务器之间的连接相关的。那么什么是一个连接?
当客户端向服务器发送 SQL 语句时,比如查询,那么就会连接到服务器,然后服务器通过连接发送响应给客户端,如查询的结果集。对于客户端连接,这样会导致一些关于连接的字符集和 校对规则的问题,这些问题均能够通过系统变量来解决,而这些变量就是上面提到的 3 个变量。 |
字符集是众所周知的,比如西欧用的 latin1,简体中文的 GB2312, GBK 等,如下所示:
[attach]733[/attach][attach]734[/attach]
还可以用 SHOW CHARACTER SET 命令列出当前可用的字符集。
那什么是“校验规则” 呢?
校对规则就是一套对编码比较的规则。最简单的校对规则就是二元(binary)校对规则,即逐字节相等,比如上图中的 gb2312_bin , gbk_bin ,latin1_bin 。
任何一个给定的字符集至少会有一个校对规则,也可能有几个,可以使用 SHOW COLLATION 命令列出一个字符集的校对规则,比如:
[attach]735[/attach]
上面的 SHOW COLLATION LIKE 'latin1%'; 命令那些名字以 latin1 开头的校对规则。比如 代表的是 “瑞典/芬兰” ,“” 代表的是 "丹麦/挪威" 。可以简单的着么认为:在一个字符集中,应用一个校对规则,可以相应的识别出一个或几个国家所使用的语言。
“校对规则“ 有以下 2 个特征:
(1) 两个不同的字符集不能有相同的校对规则。
(2) 每一个默认的字符集有一个默认的校对规则,例如 latin1 默认的校对规则是 latin1_swedish_ci 。
”校对规则“ 的命名约定为: _bin 表示”二元“, _ci 表示大小写不敏感,_cs 表示大小写敏感。
现在回过来看一下上面所提到的 3 个变量。
character_set_client 变量是指 |
作为客户端发送的查询中使用的字符集。比如上面使用的是 utf8 。 在服务器收到查询后,服务器会把 |
character_set_connection 所指定的字符集。在转换完毕后,服务器会发送结果集或错误信息到客户端,那么在此之前需要转换为那种字符集呢?是的,就是 |
character_set_results 这个变量所指定的。 |
先假设上述的几个变量分别使用的是 utf8 字符集。 |
接下来要查询一个表,表中的某个字段里有中文,查询语句如下:
[Plain Text] 纯文本查看 复制代码
select topic_title from phpbb3.phpbb_topics;
[attach]736[/attach]
可以正常显示中文。
现在运行 SET NAMES 语句改变上面 3 个值:
[Plain Text] 纯文本查看 复制代码
SET NAMES GBK;
show variables like "%char%";
[attach]737[/attach]
由上图可见,上面提到的 3 个变量的值都从原来的 utf8 改变为 gbk 了。接下来再像上面的同样查询一次: |
[Plain Text] 纯文本查看 复制代码
select topic_title from phpbb3.phpbb_topics;
[attach]738[/attach]
原来正常显示的中文乱码了。
为什么呢?
从头开始分析。首先对于运行 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;
[attach]739[/attach]
再次和上面的同样查询,可以看到:
[attach]740[/attach]
由上图可以看到,中文在终端下又恢复了显示。
下面,我们先将上面的 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;
}
?>
打开该网页后发现乱码了:
[attach]741[/attach]
道理很简单,返回是按照 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″);
注意,上面的那条语句,不会改变服务器里所里设置的变量。
欢迎光临 曲径通幽论坛 (http://www.groad.net/bbs/) |
Powered by Discuz! X3.2 |