本文转载自:http://hi.baidu.com/huabinyin/item/7f51e462df565c97c4d24929。感谢作者及相关博主。

       相信很多朋友都会对字符编码敬而远之,但一发生乱码问题却头大不已,本文结合前人的经验及Mysql手册中的解释,用具体的操作和例子,旨在了解mysql的字符编码机制以及乱码问题的解决。

【问题现象】

网页xxx.php用EditPlus另存为UTF8格式,

MySQL在my.ini里设置[ cliant ] 和 [ mysqld ] 都设置为default-character-set=utf8,

建表时加了CREATE TABLE `xxx ` (myname varchar(255)) ENGINE=MyISAM DEFAULT CHARSET=utf8,

用xxx.php执行insert/update/select出来的都是中文,

貌似没问题,但是用phpMyAdmin看select是乱码,用第三方工具软件(如SQLyog)看select也是乱码,mysqldump也是乱码,很不爽。当然,如果你建表的时候,选择了binary/varbinary/blob类型,不会发现乱码,因为指定的是二进制保存,MySQL保存数据时就没有编码的概念了。

【查找问题】

虽然在my.ini里设置default-character-set=utf8,但是执行以下命令时有新发现:

mysql> SHOW VARIABLES LIKE 'character%';

+----------------------------------------+-------------------------

| Variable_name| Value

+----------------------------------------+-------------------------

| character_set_client | latin1

| character_set_connection | latin1

| character_set_database | utf8

| character_set_filesystem | binary

| character_set_results| latin1

| character_set_server| utf8

| character_set_system| utf8

| character_sets_dir| D:\mysql\share\charsets\

+----------------------------------------+-------------------------

8 rows in set (0.00 sec)

mysql> SHOW VARIABLES LIKE 'collation_%';

+---------------------------------------+------------------

| Variable_name| Value

+---------------------------------------+------------------

| collation_connection | latin1_swedish_ci

| collation_database| utf8_general_ci

| collation_server| utf8_general_ci

+--------------------------------------+------------------

3 rows in set (0.00 sec)

发现Value列里面不全是utf8,仍然有部分是latin1,比如其中的client和connection。

其中本文会多次提及以下几个概念:

character_set_client:客户端发送的查询中所使用的字符集,下文简称client

character_set_connection:服务器接收到查询后,会将查询从character_set_client系统变量所标识的编码转换到character_set_connection,下文简称connection

character_set_results:服务器发送结果集或返回错误信息到客户端所使用的字符集,下文简称results

而这几个字符集设置的关系为:

信息输入路径:client→connection→server
信息输出路径:server→connection→results

换句话说,每个路径要经过3次改变字符集编码。以出现乱码的输出为例,按照上表中的字符集设定:server里utf8的数据,传入connection转为latin1,传入results转为latin1,页面如果设定为UTF-8的话,又把results的latin1转为UTF-8。如果两种字符集不兼容,比如latin1和utf8,转化过程就为不可逆的,破坏性的。所以就转不回来了。

再来一个输入路径的例子:从xxx.php页面上输入汉字,因为xxx.php是UTF8编码的,所以xxx.php以UTF8格式转换输入的汉字,然后以UTF8提交给mysql,但是mysql的client和connection都是latin1的,而server是UTF8的,所以mysql存储时,先将xxx.php提交的汉字,转成latin1的格式,再转成UTF8字符格式存在表中。如果此时我们用第三方软件或者phpMyAdmin去select查看此表,而表中存储的数据是被latin1过的UTF8字符,出来的时候是以UTF8格式取的,当然看起来时乱码了。解决方法就是让所有过程都是UTF8的就可以了。

【解决问题】

一、从my.ini下手

#CLIEANT SECTION

default-character-set=utf8

#SERVER SECTION

default-character-set=utf8

以上2个section都要加default-character-set=utf8,而默认这两项都为latin1

然后重启mysql,执行

mysql> SHOW VARIABLES LIKE 'character%';

mysql> SHOW VARIABLES LIKE 'collation_%';

确保所有的Value项都是utf8即可。

二、设置数据库(database)、表(table)或字段(column)的字符集(character set)及校对规则(collate)。

例如:建表时加utf8,表字段的Collation可加可不加,不加时默认是utf8_general_ci了。

CREATE TABLE `tablename4` (

`id` int(11) NOT NULL AUTO_INCREMENT,

`varchar1` varchar(255) DEFAULT NULL,

`varbinary1` varbinary(255) DEFAULT NULL,

PRIMARY KEY (`id`)

) ENGINE=MyISAM DEFAULT CHARSET=utf8

这里要说明一下:

1、在服务端,主要分为四个层次,由底到高依次为:server, database, table, column,每一次都可设定其字符集(character set)及校对(collate),如果高层的字符集有设置,则按高层的来进行字符编码,如果本层无设置编码,则采用下一层次的字符集。

因此,如果设置了database级的字符集,则table级的字符集可设可不设,如果既设置了database级又设置了table级,则按table级来存储。

2、字符集(character set)和校对的关系。

字符集是一套符号和编码。校对规则是在字符集内用于比较字符的一套规则。

用一个简单的例子来解释,如,有四个字母,A,B,C,D,我们为每个字母赋予一个数值:‘A’=0,‘B’= 1,‘a’= 2,‘b’= 3。字母‘A’是一个符号,数字0是‘A’的编码,这四个字母和它们的编码组合在一起是一个字符集。而针对某一字符集的校对规则可以有多种,例如按字母所代表的数值大小来排列,或者大小写不敏感的排列等等。

MySQL按照下面的方式选择字符集和 校对规则:

·        如果指定了CHARACTER SET X和COLLATE Y,那么采用CHARACTER SET X和COLLATE Y。

·        如果指定了CHARACTER SET X而没有指定COLLATE Y,那么采用CHARACTER SET X和CHARACTER SET X的默认校对规则。

如果指定了COLLATE Y而没有指定CHARACTER SET X,那么采用COLLATE Y相对应的CHARACTER SET和COLLATE Y。

·        否则,采用低一级服务器字符集和服务器校对规则。

三、页面文件保存时选择utf-8编码,页面上加上utf-8的编码方式:

对于静态页面,在<head>内加上:<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />

对于PHP文件:header('conten-type:text/html;charset=utf-8');

对于JSP、Servlet

a) 设置web容器的编码格式。为你的servlet的doGet或doPost方法开始处加入如下代码:
        request.setCharacterEncoding("utf-8");
        response.setCharacterEncoding("utf-8");
b) 为每个jsp页面指定其编码格式。<%@ page pageEncoding="utf-8"%>
c) 在连接数据库用的URL后加入:useUnicode=true;characterEncoding=utf-8 如:
                        url="jdbc:mysql:///db1?useUnicode=true&characterEncoding=utf-8",

如果是xml文件中     url="jdbc:mysql:///db1?useUnicode=true&amp;characterEncoding=utf-8",

四、在执行CRUD操作前先执行一下mysql_query("set names utf8");

网上很多人说要执行set names utf8,我想如果每次数据库连接都要set names一下,原本的一句sql语句变成了两句,很不合理嘛!到底要不要set names utf8还有待测试,因为我刚翻了Mysql5.1的文档,里边有写道:

SET NAMES 'x'语句与这三个语句等价:

mysql>SET character_set_client =x;

mysql>SET character_set_results =x;

mysql>SET character_set_connection =x;

而刚才在my.ini中所设置的CLIENT SECTION中的:default-character-set=utf8就已经同时修改了client、results及connection的值为utf8了。

今天刚实验了一下,页面也已经设置为UTF8的话,不需要再set names一下,在数据库客户端SQLyog也同样不需要!因为mysql初始化时已经从my.ini里读取到default_character_set=utf8并将以上三个变量都设置为utf8了。但如果页面是用别的编码的话,必须要set names为与之一致的编码

测试代码xxx.php如下:

<?php

header('conten-type:text/html;charset=utf-8');

mysql_connect("localhost", "root", "password") or die("Could not connect: " . mysql_error());

mysql_select_db("test");

mysql_query("set names utf8");//可以去掉这句!

$str = "CHN 软件开发有限公司,JPN ソフトウェア開発株式会社,KOR 소프트웨어 개발 유한 공사,RUS Суд программного обеспечения".time();

$sql = "insert into tablename4 (varchar1, varbinary1 ) values ('".$str."','".$str."')";

echo $sql."<hr>";

mysql_query($sql);

$result = mysql_query("SELECT id, varchar1 ,varbinary1 FROM tablename4");

while ($row = mysql_fetch_array($result, MYSQL_BOTH)) {

printf ("ID: %s , varchar1: %s, varbinary1: %s<br>", $row[0], $row["varchar1"], $row["varbinary1"]);

}

mysql_free_result($result);

?>

如此设置之后,无论是在php页面插入任何utf8字符,在php页面里取出来的,在phpMyAdmin里取出来的,在mysql的第三方客户端软件里取出来的,都是一样的汉字了,不会再发现乱码,mysqldump出来的也是汉字。OK,问题解决。

【CMD那些事儿】

        首先,在中文windows系统下,在cmd.exe里运行mysql.exe,有点特别。因为默认情况下,中文windows系统cmd.exe里的字符编码是cp936即GBK,不能显示全部UTF8字符,所以在字符终端里看到乱码是正常现象,不要奇怪,这个问题在类Unix系统的shell终端里可以解决的。

        其次,由于CMD的输入输出格式不能改变,中文系统为GBK(可以将CMD与网页浏览器对比一下,浏览器会根据网页文件的字符编码来改变输入输出的字符编码),因此,如果希望在CMD里敲insert、select等输入输出中带有中文字符的语句时,必须保证myqsl的client、connection和results的字符编码为GBK或gb2312,方法是输入命令:set names gbk。如果是从外部SQL文件以Source方式导入到数据库的话,则要保证client、connection的字符编码与外部SQL文件的编码方式一致(可用记事本打开-->另存为 来查看文件的编码格式),如果文件的编码格式不是GBK,例如UTF-8,则要在source之前先set names utf8; 这样,就能正确将中文数据导入至数据库了(但如果此时在CMD里select一下,仍然会显示乱码,原因是此时的results编码格式为utf8,不过没关系,保存在mysql的数据是正确的,只是查询结果集以utf8方式返回到编码格式为GBK的CMD中导致乱码而已),有点啰嗦,希望明白,呵呵!

这里可是印象深刻啊,今天帮一个实习小妹子解决一个导入数据库乱码问题,我一拿到数据库文件,首先检查表语句,确认用的是UTF8编码,然后我开始用cmd命令行方式导入数据库(我的数据库中的client, connection, result, database, server的字符集都为UTF8),我先set names utf8; 然后source导入,没报错,select发现中文乱码。于是我查看SQL文件的编码格式为UTF-8,心想,没错了啦!

百思不得期解,于是上网得知中文Windows系统下的cmd.exe的输入格式都为GBK,于是我又set names gbk;再一次source导入,报错:

ERROR 1406 (22001): Data too long for column 'XXX' at row XX

ERROR 1366 (HY000): Incorrect string value: '\xAB\xA5...' for column 'XXX' at row XX

突然想起SQL文件为UTF-8格式,尝试将其改为ANSI,再次source, 无报错,SELECT也显示中文了。

细心想了一下,自认为的解释是:

第一次导入:set names utf8; 然后source导入,没报错,select发现中文乱码

原因:没报错是因为我的SQL文件本身为UTF-8编码,而我又set names utf8, 即client, connection, result 都为utf8, 编码一致,可以认为数据正确存储到数据库了,而CMD中select为乱码的原因为CMD的编码无法改变,仅为GBK,尽管这时候client, connection, result都为UTF8编码,但最终显示的格式却是GBK!编码不一致,当然出现乱码了,但我猜想这种情况下,数据库里的数据是没问题的!明天再实验一下!

经今早实验,我把数据库drop掉再重新按这种方式导入,没报错,虽然在CMD里select仍然发现乱码,但在SQLyog等客户端是正常显示的,证明我的猜想是对的!

第二次导入:set names gbk;再一次source导入,报错。

原因:这时SQL文件的编码格式为UTF-8,而由于set names gbk导致 client, connection, result都改变为GBK,你声明client的输入格式为GBK,却拿个UTF-8格式的文件给我导入,输入格式不一致导致报错!

第三次导入:先修改SQL文件编码为ANSI,最后source, 无报错,select也是正常的。

原因:我在第二次导入时set names gbk; 这时client, connection, result都为gbk,而且SQL文件格式也为兼容GBK,于是就成功了!

最后总结一下:同时多看看MYSQL手册,了解一下MYSQL的理论,这样遇到错误的时候分析也有理论基础。还有的是建议看英文文档的,并不是说中文的不好,只是认为人总会犯错的,翻译过程难免有错漏,哥今天看中文的手册就发现有一处翻译错误,而且是严重误导性的,上网翻英文版的又发现中文版的漏翻了一些……

Mysql 的字符编码机制、中文乱码问题及解决方案【转载】的更多相关文章

  1. 前端网页、php与mysql数据库字符编码(解决中文等乱码问题)

    web开发中经常涉及前端网页——php——mysql之间的数据交互,当数据只有英文时通常不会有什么问题,但一旦涉及中文,三个地方的某一处字符编码不一致(如,网页使用的时gbk而mysql使用utf-8 ...

  2. SpringBoot(八):SpringBoot中配置字符编码 Springboot中文乱码处理

    SpringBoot中配置字符编码一共有两种方式 方式一: 使用传统的Spring提供的字符编码过滤器(和第二种比较,此方式复杂,由于时间原因这里先不介绍了,后续补上) 方式二(推荐使用) 在appl ...

  3. Linux下修改MySQL数据库字符编码为UTF-8解决中文乱码

    由于MySQL编码原因会导致数据库出现乱码. 解决办法: 修改MySQL数据库字符编码为UTF-8,UTF-8包含全世界所有国家需要用到的字符,是国际编码. 具体操作: 1.进入MySQL控制台 &g ...

  4. ubuntu下修改mysql默认字符编码出现的Job failed to start解决办法

    ubuntu下修改mysql默认字符编码出现的Job failed to start解决办法 前几天卸掉了用了好多年的Windows,安装了Ubuntu12.04,就开始各种搭环境.今天装好了MySQ ...

  5. MySQL的字符编码体系(二)——传输数据编码

    MySQL的字符编码体系能够分成两部分:一部分是关于数据库server本身存储数据表时怎样管理字符数据的编码:还有一部分是关于client与数据库server数据传输怎样编码.上一篇MySQL的字符编 ...

  6. MAC下 mysql不能插入中文和中文乱码的问题总结

    MAC下 mysql不能插入中文和中文乱码的问题总结 前言 本文中所提到的问题解决方案,都是基于mac环境下的,但其他环境,比如windows应该也适用. 问题描述 本文解决下边两个问题: 往mysq ...

  7. (转)MySQL 插入数据时,中文乱码问题的解决

    MySQL 插入数据时,中文乱码问题的解决  原文:http://www.cnblogs.com/sunzn/archive/2013/03/14/2960248.html 当向 MySQL 数据库插 ...

  8. 拨开字符编码的迷雾--MySQL数据库字符编码

    拨开字符编码迷雾系列文章链接: 拨开字符编码的迷雾--字符编码概述 拨开字符编码的迷雾--编译器如何处理文件编码 拨开字符编码的迷雾--字符编码转换 拨开字符编码的迷雾--MySQL数据库字符编码 1 ...

  9. MySQL的字符编码体系(一)——数据存储编码

    安装MySQL好多次了,每次都会纠结于数据库的字符编码配置,所以我决定这一次彻底把它理清. MySQL的字符编码结构比較细,它慷慨向分为两个部分:数据存储编码和传输数据编码.本篇讨论数据存储编码部分, ...

随机推荐

  1. Linux后台开发面试问题汇总

    个人从事安全后台开发,当然是linux环境下的了.举几个常见的问题.1. 数据结构基础.比如实现一个最简单的哈希表.2. 操作系统基础.linux进程模型,堆/栈的区别,大概的位置,各往哪个方向生长, ...

  2. RuntimePermissions

    This sample shows runtime permissions available in Android M and above. Display the log on screen to ...

  3. Android 开源项目 eoe 社区 Android 客户端(转)

    本文内容 环境 开源项目 eoe 社区 Android 客户端 本文介绍 eoe 社区 Android 客户端.它是一个开源项目,功能相对简单,采用侧边菜单栏.可以学习一下.点击此处查看 GitHub ...

  4. 数据库 ORM框架 ORMLite

    几个ORM框架的比较 先介绍一下ORM的概念,以前也一直听说,不过没详细了解啥意思.其全称叫做对象关系映射(Object Relation Mapping),是一种程序设计技术,用于实现面向对象编程语 ...

  5. 谷歌postman插件用不了的命令行指令

    谷歌postman插件用不了,想测试通过post提交传过来的参数测试,打开所在目录,shift右键,打开命令窗口,输入一下指令,即可获取到提交的值curl -X POST -d 'name=kpf&a ...

  6. HTTPS双向认证

    生成证书 openssl genrsa -des3 -out server.key 2048 openssl req -new -x509 -key server.key -out ca.crt -d ...

  7. Oracle oerr使用

    [oracle@cuug ~]$ oerr ora 01555 01555, 00000, "snapshot too old: rollback segment number %s wit ...

  8. iOS8中添加的extensions总结(一)——今日扩展

    通知栏中的今日扩展 分享扩展 Action扩展 图片编辑扩展 文件管理扩展 第三方键盘扩展 注:此教程来源于http://www.raywenderlich.com的<iOS8 by Tutor ...

  9. 基于ThinkPHP+AJAX的省市区三级联动

    练习,就当练习. 省市区三级联动,样式如下图所示: 1,导入两个js文件并且导入数据库文件. 两个js文件分别是jquery-2.1.4.min.js和jquery-1.js,数据库文件,见附件. 2 ...

  10. 【USACO 3.1.3】丑数

    [描述] 对于一给定的素数集合 S = {p1, p2, ..., pK}, 来考虑那些质因数全部属于S 的数的集合.这个集合包括,p1, p1p2, p1p1, 和 p1p2p3 (还有其它).这是 ...