[实践OK]mysql连接字符串乱码之mysql连接通道中的字符集和校验规则---来源:淘宝团队,[转]深入Mysql字符集设置,比阿里巴巴技术团队的更形像 。
https://www.cnblogs.com/wcwen1990/p/6917109.html
最后谈严格模式:Mysql尽可能采用严格模式,一定程序上防止一些编码问题和长度问题(https://blog.csdn.net/fdipzone/article/details/50616247)。
set global sql_mode=''; #这种方法修改,当前会话失效后,这个修改就没意义了,建议修改配置文件的方式修改,修改后,不需要重启mysql服务,立即生效,检查是否生效,执行sql ,select @@sql_mode;
show create table t;
CREATE TABLE `t` (
`c` varchar(255) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8
show variables like "%char%";
set names gbk;
mysql> select c ,hex(c) from t;
+------+--------+
| c | hex(c) |
+------+--------+
| 东 | E4B89C |
| 东 | E4B89C |
| 东 | E4B89C |
| 东 | E4B89C |
| 东 | E4B89C |
| 东 | E4B89C |
| 东 | E4B89C |
| 东 | E4B89C |
+------+--------+
8 rows in set (0.00 sec)
php igbk.php
string(4) "b6ab"
int(1)
string(4) "b6ab"
string(4) "b6ab"
Array
(
[0] => Array
(
[c] => 东
[0] => 东
[hex] => E4B89C
[1] => E4B89C
)
说明三个问题:
1)咱们在显示一个字符时,在SecureCRT里配置的character encoding只是显示,并不像Mysql的set names utf8里的三个参数(character_set_client character_set_connection character_set_results )有由一个字符转向另一个字符集的功效,要想显示正常,则只需要对转出来的结果进行匹配即可显示正常。bin2hex("东")
三条:
一)如果character_set_client和character_set_results 一般情况下要一致,因为一个表示客户端发送的数据格式,另一个表示客户端接受的数据格式为了避免造成数据丢失,需让 character_set_connection的字符编码 大于 character_set_client的字符编码.(MySQL Server收到请求时将请求数据从character_set_client转换为character_set_connection;)
二)对于Mysql旧版本的默认latin情况,里面尽管传入了UTF8,也能正常的显示和插入(character_set_client| latin1(中文你utf8是3个字节,也就是来自前端网页的UTF8的你字,而latin1是按照单个字节解析的,虽然进行了转换,但不会导致二进制内容的变化,但实际上mysql客户端认为我输入了3个latin1字符;还好是UTF8前端,如果客户端是GBK,直接收到一个3字节的UTF8的编码会为是1个gbk字符和一个西欧字符) -> character_set_connection | latin1->),但本质上是以UTF8的方式存入磁盘时是6个字符( character_set_database | utf8 ),而出来时又被Latin1转回为3个字符后被UTF8看成1个汉字也就是你字,而如果这个Lation1不这样6字节变3字节,也就是(character_set_results 的值由latin1变UTF8后)UTF8后,则6个字符以UTF8直接穿透到显示层了,会在SecureCRT(设置UTF8输出显示)看到UTF8是6个字符认为是两个汉字,也就成了我们认为的乱码!!!归结于:由于latin1 是一种非常宽松的编码方式,任何一种编码方式得到的文本,用 latin1 进行解码,都不会发生解码失败——当然,解码得到的结果自然也就是理所当然的“乱码”。
实践如下,实践证明输出与character_set_results的编码有关,当set character_set_results是latin1时会转为输入时的UTF8的三个字符,而set character_set_results="utf8"时,则它输出了存入Mysqld数据库的6个字节的UTF8直接输出,前端认为是两个字也就显示出来了东,也就是把C3A4C2B8C593以SecureCRT配置的前端输出经过UTF8解码显示,就是乱码了。
这个在Mysql里可通过设置set names latin1;并且secureCRT的Character encoding设置为utf8就能正常显示,本质上:
mysql> select c ,hex(c) from t;
+--------+--------------+
| c | hex(c) |
+--------+--------------+
| 东 (这里面的输出是将 hex(c)里的内容C3A4C2B8C593 经过character_set_results=latin1编码后显示,这个显示正常不正常是由SecureCRT的输出时是GB2312,还是UTF8,这儿输出本来是latin1,但是被SecureCRT按utf8显示后就是 三个字节的东字!!! ) | C3A4C2B8C593 |
+--------+--------------+
核心内容:输出的那个Lation1变UTF8(set character_set_results=utf8)后,反而乱码的原因是如下:
From:https://edu.aliyun.com/a/15790
基础学习:关于前面提到的字节和位的一个关系的一个基础知识:位:"位(bit)"是电子计算机中最小的数据单位。每一位的状态只能是0或1。字节:8个二进制位构成1个"字节(Byte)",它是存储空间的基本计量单位。1个字节可以储存1个英文字母或者半个汉字,换句话说,1个汉字占据2个字节的存储空间。即1个字节等于8个比特(1Byte=8bit)。八位二进制数最小为00000000,最大为11111111(0XFF,全是1,也就是两个F(十进制是255 ),C3的对应的十进制195,A4对应十进制是164);通常1个字节可以存入一个ASCII码,“c3a4 c2bd c2a0”(6个字节) ,ASCII码用十六进制表示的,C3、a4等等,http://ascii.911cha.com/。
三)对于2的这种情况是在升级时可全部改为UTF8的编码的,是可以根据 mysqldump 时加上 --default-character-set=latin1,导出正常的Utf8编码的中文且显示正常的字符,但是加了这 个后,记得修改里面的set names latin1,为set names utf8, 而在程序代码里连接时也加上set names utf8,黄冶是default-character-set=utf8就能正常显示。(mysqldump -h 127.0.0.1 -P 3306 -u username --default-character-set=latin1 -p databasename > dumpfile.txt)
=========================================================================================
[转]深入Mysql字符集设置,比阿里巴巴技术团队的更形像,有图:
https://blog.csdn.net/haiross/article/details/51273630 (请道因为UTF8插入时如果存的是比它小的,可能转时出现问题,而没法恢复,因为存入时就坏了:插入数据将经过utf8=>utf8=>latin1的字符集转换,若原始数据中含有/u0000~/u00ff范围以外的Unicode字符,会因为无法在latin1字符集中表示而被转换为“?”(0x3F)符号,以后查询时不管连接字符集设置如何都无法恢复其内容了。而以latin1入到Utf8后,会导致字符数变多,也就是存储空间会因为中文的UTF8是3个及以上(文章里提到的3个字节变成6个字节),占用了更多磁盘,插入操作的数据将经过latin1(character_set_client)=>latin1(character_set_connection)=>utf8(character_set_results)的字符集转换过程,这一过程中每个插入的汉字都会从原始的3个字节变成6个字节保存; )
阿里云大学谈到了其乱码的本质一样的用你的UTF8作描述的文字版:https://edu.aliyun.com/a/15790
MySQL的几个character_set变量的说明:https://www.linuxidc.com/Linux/2017-04/143263.htm
MySQL抓包工具:MySQL Sniffer【转】:
https://www.cnblogs.com/zhoujinyi/p/6497231.html
利用tcpdump对mysql进行抓包操作技巧_Mysql:
https://edu.aliyun.com/a/15214
特殊变量之character_set_system:character_set_system是个只读数据不能更改。也没多少改的意义,他是元数据的编码,相信不会有人用中文做数据库名和字段名之类的吧,这个字段和具体存储的数据无关。
mysql> show variables like '%char%';
+--------------------------+-----------------------------------------------------+
| Variable_name | 说明 |
+--------------------------+-----------------------------------------------------+
| character_set_client | 客户端字符集 |
| character_set_connection | 当前连接字符集 |
| character_set_database | 数据库字符集 |
| character_set_filesystem | 文件系统字符集,不要修改,使用binary即可 |
| character_set_results | 返回结果集字符集 |
| character_set_server | 服务器默认字符集,当数据库、表、列没有设置时,如Show create table里的中文表字段批注 |
| | 默认使用此字符集 |
| character_set_system | 固定为utf8 |
+--------------------------+-----------------------------------------------------+
| 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 |
------------------------而latin1是按照单个字节解析的,虽然进行了转换,但不会导致二进制内容的变化!-------------------------------------------
这里首先需要解释的是,我想应该就是连接通道的含义了。那什么是连接通道呢?
背景:这种理解其实是早期程序员以PHP为例通常觉得数据存为简体中文比教少占空间,utf8中文占用大于等于三,由unicode字符集决定的,其实现在看来用utf8都不是啥大事,磁盘很便宜。再就是早期mysql默认就是拉丁字符集合。
导致出现想转为utf8的,首先导出时候得指定字符集为拉丁,假如不加指定以拉丁导出其默认是utf8,往往那个建表结构的comment注释是utf8,出现评论正常,数据库内容中文乱码,用file xxx.sql检查的确是一个utf8的sql文件。
如果MySQL服务器上的字符集是 latin1 的,mysqldump 出来的中文都是乱码!一个简单的办法是加上默认字符集选项,如:
mysqldump -h 127.0.0.1 -P 3306 -u username --default-character-set=latin1 -p databasename > dumpfile.txt #character_set_connection、character_set_client、character_set_results三个字符集一次性被设置为latin1了。character_set_client、character_set_connection、character_set_results这3个参数值是由客户端每次连接进来设置的,和服务器端没关系。我们登陆进mysql后,执行 set names utf8; 实际就是同时修改上面的这3个参数值的。From:MySQL的几个character_set变量的说明,https://www.linuxidc.com/Linux/2017-04/143263.htm。
有些网站在开发时,并没有正确指定编码,採用了预设的 latin1 ,并且将 big5 的中文资料直接储存。这种情况如果直接用 mysqldump 直接匯出,结果可能是一堆乱码,必须加上 --default-character-set=latin1 让匯出的资料保持正常的 big5 编码。
mysqldump -u USER -p DBNAME --default-character-set=latin1 > DBNAME.sql
如果在匯出备份资料时,忘记加上参数,產生的乱码资料,用 iconv 可以救回部分(以下指令示范假设匯出档為错误编码的utf8),参数 "-c" 表示忽略无法转码的字元。
iconv -c -f utf8 -t latin1 DBNAME.sql > DBNAME.big5.sql
对于 big5 编码的匯出档,也可以用 iconv 转换成 utf8 格式。
iconv -c -f big5 -t utf8 DBNAME.sql > DBNAME.utf8.sql
再进行编码格式设定的替换。
perl -pi -e 's/big5/utf8/g' DBNAME.utf8.sql
旧的Mysql默认选项:
mysql> show variables like "%set%";
+--------------------------+----------------------------+
| Variable_name | Value |
+--------------------------+----------------------------+
| character_set_client | latin1 |
| character_set_connection | latin1 |
| character_set_database | latin1 |
| character_set_results | latin1 |
从源码编译mysql的时候,通过编译参数进行设置
编译的时候如果指定了-DDEFAULT_CHARSET和-DDEFAULT_COLLATION,那么:
创建database、table时,会将其作为默认的字符集/字符序。
client连接server时,会将其作为默认的字符集/字符序。(不用单独SET NAMES)
shell> cmake . -DDEFAULT_CHARSET=utf8 \
-DDEFAULT_COLLATION=utf8_general_ci
RPM打包:
cmake . \
...
-DDEFAULT_CHARSET=utf8 \
...
make %{?_smp_mflags}
=============================================================================
搞成utf8的编码,怎么办?分三步:
1)mysqldump default-character-set=latin databse table > /tmp/utf8format.sql #本质上是存在数据库里面的编码就是latin,指定以Latin字符编码给导出到一个utf8文件里面。
2)在导出文件里面因为1加入了指定导出编码了,于是文件里面sql语句有一个set names latin,把这个改成set names utf8。此时用less看无论建表结构的评论注释还是表里内容均显示正常,中文不乱码。
3)在导入数据的mysqlserver的my.cnf设置尽可能全utf8,如果不设置因为2在sql文件建立表和插入数据前加入设置编码了,直接mysql导入即可。保证全是utf8后,PHP的连接字符串编码也是UTF8即可。
所谓连接通道,就是客户端和服务器端保持连接的一个通道,它是逻辑上的一个概念。客户端通过连接通道发送sql语句到服务器端,服务端执行,将结果再通过连接通道返回至客户端。the connection is the pass when you connect to the server.
这个过程中,有几个临界点(逻辑上概念),是我们需要注意的,mysql也就在这几个临界点上做了文章。
1、当语句离开客户端的时候:
从客户端出来的,包括sql语句本身(这里里面就包含字符串和关键字等了),以及character_set_client系统变量。为什么要包含这个变量呢?这个变量的作用说明2点,也是它的作用:一是表示该语句中的字符集是使用character_set_client指定的字符集编码的,二是通过此系统变量来告诉服务器所发送来的语句中的字符集编码。
2、当服务器端接受到客户端的语句的时候:
mysql会使用character_set_connection/collation_connection指定的字符集以及校验规则,将客户端的字符串,做一个从character_set_client到character_set_connection的转换。
3、当服务器处理好结果以后,在把结果传给客户端前:
mysql会先将结果转换成character_set_results指定的字符集,然后传回给客户端。
当字符串在mysql服务器的时候,最终以什么格式存储到mysql数据库中,这个是受到具体的数据表级别、列级别字符集设置的控制了。
从上面的介绍中,我们就知道和连接通道相关几个参数了,他们分别是character_set_client/connection/results,可以如下查看:
mysql> show variables like 'char%';
+--------------------------+-------------------+
| Variable_name | Value |
+--------------------------+-------------------+
| character_set_client | latin1 |
| character_set_connection | latin1 |
| character_set_database | gbk |
| character_set_results | latin1 |
| .........................| ...... |
+--------------------------+-------------------+
8 rows in set (0.00 sec)
mysql> show variables like 'colla%';
+----------------------+-------------------+
| Variable_name | Value |
+----------------------+-------------------+
| collation_connection | latin1_swedish_ci |
| collation_database | gbk_bin |
| collation_server | latin1_swedish_ci |
+----------------------+-------------------+
3 rows in set (0.00 sec)
下面我们来做个实验,来证明一下这个结果:
首先保证character_set_connection与character_set_results以及底层存储字符集的一致性,看看character_set_client的效果。
mysql> show variables like 'char%';
+--------------------------+------------------+
| Variable_name | Value |
+--------------------------+------------------+
| character_set_client | latin1 |
| character_set_connection | gbk |
| character_set_database | gbk |
| character_set_results | gbk |
| .........................| ...... |
+--------------------------+------------------+
8 rows in set (0.00 sec)
mysql> create table t (a varchar(10)); -- 这里没有指定字符集,就默认使用了database的字符集gbk了
Query OK, 0 rows affected (0.08 sec)
mysql> insert into t values('中国');
Query OK, 1 row affected (0.00 sec)
mysql> select * from t;
+-------+
| a |
+-------+
| ???ú |
+-------+
1 row in set (0.00 sec)
由此可以看到由于latin1与gbk对汉字的编码方式不一样(或者说latin1根本就不能正确编码汉字),在这个collection过程中,从character_set_client到character_set_connection转换时,就把你输入的好好的汉字转换成乱码了。那么,如果让过程不发生转换呢?
mysql> set character_set_client=gbk;
Query OK, 0 rows affected (0.00 sec)
mysql> insert into t values('人民');
Query OK, 1 row affected (0.02 sec)
mysql> select * from t;
+-------+
| a |
+-------+
| ???ú |
| 人民 |
+-------+
2 rows in set (0.00 sec)
可见,这里是能正确存储和显示的,很简单,因为任何转换都没有发生,当然就不会出现乱码了。
接着做实验,我们让character_set_connection发生变化:
mysql> set character_set_connection=latin1;
Query OK, 0 rows affected (0.00 sec)
mysql> insert into t values ('共和国');
Query OK, 1 row affected (0.00 sec)
mysql> select * from t;
+-------+
| a |
+-------+
| ???ú |
| 人民 |
| ??? |
+-------+
3 rows in set (0.00 sec)
可见,这里character_set_client=gbk,character_set_connection=latin1,character_set_database=gbk。首先,client到connection过程中,汉字被转换成乱码(这个过程可能就会丢失信息);然后存储数据的时候,又从connection到存储的字符集(gbk)发生一次转换,乱码被转换成“更”乱码。这里如果connection与client的字符集有种包容性关系的话,如character_set_client=gbk, character_set_connection=utf8,character_set_results=gbk,底层存储也是gbk编码,由于utf8“兼容”所有的字符集,故在转换过程中不会发生信息丢失,查询的时候也不会是乱码,如下:
mysql> show variables like 'char%';
+--------------------------+-----------------+
| Variable_name | Value |
+--------------------------+-----------------+
| character_set_client | gbk |
| character_set_connection | utf8 |
| character_set_database | gbk |
| character_set_results | gbk |
| .........................| ...... |
+--------------------------+-----------------+
8 rows in set (0.00 sec)
mysql> create table t (a varchar(10)) charset=gbk;
Query OK, 0 rows affected (0.05 sec)
mysql> insert into t values('中国');
Query OK, 1 row affected (0.00 sec)
mysql> select * from t;
+------+
| a |
+------+
| 中国 |
+------+
1 rows in set (0.00 sec)
我来解释一下这个过程,假设“中国”的gbk编码是1234,而utf8的编码是4321,utf8的编码为1234的假设是“淘宝”。client的“中国”的编码1234进入connection,仍然是1234(但实际上它的含义已经发生变化为“淘宝”),存储的时候connection的1234编码到表存储也是1234,刚好正确表达了“中国”的意思,查询返回时,由于results也是gbk,所以不发生转换,正确显示。很显然,如果results又是一种与gbk不兼容的字符集如latin1,查询又会出问题,如下:
mysql> set character_set_results=latin1;
Query OK, 0 rows affected (0.00 sec)
mysql> select * from t;
+------+
| a |
+------+
| ?? |
+------+
1 rows in set (0.00 sec)
当我们改变底层存储的字符集的时候,会怎样?请看如下实验:
mysql> show variables like 'char%';
+--------------------------+---------------+
| Variable_name | Value |
+--------------------------+---------------+
| character_set_client | gbk |
| character_set_connection | gbk |
| character_set_database | gbk |
| character_set_results | gbk |
| .........................| ...... |
+--------------------------+---------------+
8 rows in set (0.01 sec)
mysql> create table t (a varchar(10)) charset=latin1;
Query OK, 0 rows affected (0.05 sec)
mysql> insert into t values('淘宝');
Query OK, 1 row affected, 1 warning (0.02 sec)
mysql> select * from t;
+------+
| a |
+------+
| ?? |
+------+
1 row in set (0.00 sec)
很显然,由于latin1字符集无法存储汉字,故出现乱码。
下面,我们只改变results字符集,看看效果如何:
mysql> show variables like 'char%';
+--------------------------+----------------+
| Variable_name | Value |
+--------------------------+----------------+
| character_set_client | gbk |
| character_set_connection | gbk |
| character_set_database | gbk |
| character_set_results | latin1 |
| .........................| ...... |
+--------------------------+----------------+
8 rows in set (0.00 sec)
mysql> create table t (a varchar(10)) charset=gbk;
Query OK, 0 rows affected (0.08 sec)
mysql> insert into t values('淘宝');
Query OK, 1 row affected (0.00 sec)
mysql> select * from t;
+------+
| a |
+------+
| ?? |
+------+
1 row in set (0.00 sec)
很显然,底层为gbk编码的字符串转换成latin1的字符集的字符,变成乱码,传给客户端,再次转换成gbk字符集,变成“更”乱码,故显示乱码。
下面,我再介绍几个“简约”命令。
SET NAMES ‘x’,(SET NAMES ‘charset_name’ COLLATE ‘collation_name’)相当于:
SET character_set_client = x;
SET character_set_results = x;
SET character_set_connection = x;
实验如下:
mysql> show variables like 'char%';
+--------------------------+----------------+
| Variable_name | Value |
+--------------------------+----------------+
| character_set_client | gbk |
| character_set_connection | gbk |
| character_set_results | latin1 |
| .........................| ...... |
+--------------------------+----------------+
8 rows in set (0.00 sec)
mysql> set names 'ascii';
Query OK, 0 rows affected (0.00 sec)
mysql> show variables like 'char%';
+--------------------------+----------------+
| Variable_name | Value |
+--------------------------+----------------+
| character_set_client | ascii |
| character_set_connection | ascii |
| character_set_results | ascii |
| .........................| ...... |
+--------------------------+----------------+
SET CHARACTER SET ‘x’,相当于:
SET character_set_client = x;
SET character_set_results = x;
SET collation_connection = @@collation_database;
这里collation_connection = @@collation_database的意思就是把collation_connection的设置的collation_database的值,由于collation肯定能确定character set,故其又相当于多做了个设置:把character_set_connetion设置成character_set_database的值。请看如下实验:
mysql> show variables like 'char%';
+--------------------------+-------------+
| Variable_name | Value |
+--------------------------+-------------+
| character_set_client | latin1 |
| character_set_connection | latin1 |
| character_set_database | gbk |
| character_set_results | latin1 |
| ....................... | ...... |
+--------------------------+-------------+
8 rows in set (0.00 sec)
mysql> show variables like 'collat%';
+----------------------+-------------------+
| Variable_name | Value |
+----------------------+-------------------+
| collation_connection | latin1_swedish_ci |
| collation_database | gbk_bin |
| collation_server | latin1_swedish_ci |
+----------------------+-------------------+
3 rows in set (0.00 sec)
mysql> set character set 'ascii';
Query OK, 0 rows affected (0.00 sec)
mysql> show variables like 'char%';
+--------------------------+-------------+
| Variable_name | Value |
+--------------------------+-------------+
| character_set_client | ascii |
| character_set_connection | gbk |
| character_set_database | gbk |
| character_set_results | ascii |
| ....................... | ...... |
+--------------------------+-------------+
8 rows in set (0.00 sec)
mysql> show variables like 'collat%';
+----------------------+-------------------+
| Variable_name | Value |
+----------------------+-------------------+
| collation_connection | gbk_bin |
| collation_database | gbk_bin |
| collation_server | latin1_swedish_ci |
+----------------------+-------------------+
3 rows in set (0.00 sec)
通过以上的介绍,所以为了防止出现乱码,我们可以把character_set_client,character_set_connection,character_set_database,character_set_results设置成同样的值,把collation_connection和collation_database也设置成同样的值,这样就“一劳永逸”了。程序连接mysql的时候,一般都会显示设置character_set_client的值,如java连接中,一般都有如下的一段代码来显示设置这个值:
jdbc:mysql://10.1.6.174:3306/notify?connectTimeout=1000&characterEncoding=utf8
转:
http://rdc.taobao.com/blog/dba/html/184_mysql_connect_charset_collation.html
作者:叶金荣(Email:imysql@gmail.com),来源:http://imysql.cn,转载请注明作者和出处,并且不能用于商业用途,违者必究。
首先,这片文章纯粹是我的个人经验之谈,适用于我常见的环境及项目中.
个人建议,数据库字符集尽量使用utf8(HTML页面对应的是utf-8),以使你的数据能很顺利的实现迁移,因为utf8字符集是目前最适合于实现多种不同字符集之间的转换的字符集,尽管你在命令行工具上可能无法正确查看数据库中的内容,我依然强烈建议使用utf8作为默认字符集.
接下来是完整的一个例子:
1.创建数据库表
mysql>CREATE DATABASE IF NOT EXISTS my_db default charset utf8 COLLATE utf8_general_ci;
#注意后面这句话 "COLLATE utf8_general_ci",大致意思是在排序时根据utf8校验集来排序
#那么在这个数据库下创建的所有数据表的默认字符集都会是utf8了
mysql>create table my_table (name varchar(20) not null default '')engine=MyISAM default charset utf8 default charset utf8;
#这句话就是创建一个表了,制定默认字符集为utf8
2.写数据
例子1是通过php直接插入数据:
a.php
<?php
mysql_connect('localhost','user','password');
mysql_select_db('my_db');
//请注意,这步很关键,如果没有这步,所有的数据读写都会不正确的
//它的作用是设置本次数据库联接过程中,数据传输的默认字符集
//其他编程语言/接口也类似,例如 .net/c#/odbc
//jdbc则设置连接字符串为类似"jdbc:mysql://localhost/db?user=user&password=123456&useUnicode=true&characterEncoding=UTF-8"
mysql_query("set names utf8;");
//必须将gb2312(本地编码)转换成utf-8,也可以使用iconv()函数
mysql_query(mb_convet_encoding("insert into my_table values('测试');", "utf-8", "gb2312"));
?>
例子是通过页面提交插入数据2:
b.php
<?php
//输出本页编码为utf-8
header("content-type:text/html; charset=utf-8");
mysql_connect('localhost','user','password');
mysql_select_db('my_db');
if(isset($_REQUEST['name'))
{
//由于上面已经指定本页字符集为utf-8了,因此无需转换编码
mysql_query(sprintf("insert into my_table values('%s');", $_REQUEST['name']));
}
$q = mysql_query("select * from my_table");
while($r = mysql_fetch_row($q))
{
print_r($r);
}
?>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<form action="" method="post">
<input type="text" name="name" value="">
<input type="submit" value='submit'>
</form>
自此,使用utf8字符集的完整的例子结束了.
如果你想使用gb2312编码,那么建议你使用latin1作为数据表的默认字符集,这样就能直接用中文在命令行工具中插入数据,并且可以直接显示出来.而不要使用gb2312或者gbk等字符集,如果担心查询排序等问题,可以使用binary属性约束,例如:
create table my_table ( name varchar(20) binary not null default '')engine=MyISAM default charset utf8 default charset latin1;
附1:旧数据升级办法
以原来的字符集为latin1为例,升级成为utf8的字符集。原来的表: old_table (default charset=latin1),新表:new_table(default charset=utf8)。
第一步:导出旧数据
mysqldump --default-character-set=latin1 -hlocalhost -uroot -B my_db --tables old_table > old.sql
第二步:转换编码(类似unix/linux环境下)
iconv -t utf-8 -f gb2312 -c old.sql > new.sql
或者可以去掉 -f 参数,让iconv自动判断原来的字符集
iconv -t utf-8 -c old.sql > new.sql
在这里,假定原来的数据默认是gb2312编码。
第三步:导入
修改old.sql,在插入/更新语句开始之前,增加一条sql语句: "SET NAMES utf8;",保存。
mysql -hlocalhost -uroot my_db < new.sql
大功告成!!
附2:支持查看utf8字符集的MySQL客户端有
1.) MySQL-Front,据说这个项目已经被MySQL AB勒令停止了,不知为何,如果国内还有不少破解版可以下载(不代表我推荐使用破解版 :-P)。
2.) Navicat,另一款非常不错的MySQL客户端,汉化版刚出来,还邀请我试用过,总的来说还是不错的,不过也需要付费。
3.) PhpMyAdmin,开源的php项目,非常好。
4.) Linux下的终端工具(Linux terminal),把终端的字符集设置为utf8,连接到MySQL之后,执行 SET NAMES UTF8; 也能读写utf8数据了。
附3:直接使用MySQL提供的 ALTER 语法转换字符集
这对广大非utf8又想转成utf8的用户来说,是个天大的喜讯,我也是在学习MySQL手册是才发现的。具体用法如下:
ALTER TABLE OLD_TABLE CONVERT TO CHARACTER SET charset_name [COLLATE collation_name];
转换之前,记得要先备份旧表,以防万一。更多详情请查看相关版本的MySQL手册。下面是一个实际的例子:
ALTER TABLE `t_yejr` CONVERT TO CHARACTER SET UTF8;
这个方法应该是从MySQL 4.1才开始提供的,大家可以检查一下自己的版本是否支持,如果不支持,只好按照上面提到的转换了。enjoy it!!!
http://www.sun023.com/post/108.html
再摘录自同事的一点更简洁明了的看法:
在web开发中,经常会遇到中文显示为乱码的情况。这里简单总结一下在遇到这种问题时分析和解决的思路。
1. 不论数据库是何种编码,只要在取出数据时设置的编码和存入数据时设置的编码一致,就不会出现乱码现象。
Mysql数据字符集分成四个级别:服务器级、数据库级、表级和连接级。通过status命令可以看到自己的mysql的这些设置。前三者主要控制数据在服务器端的存储。这些数据集的设置有从上至下的包含关系,也就是说,如果表没有做特设的字符集设置,则会继承数据库级的设置。而连接级字符集主要是用于客户端和服务器端的数据交互。
执行sql语句时数据的流转路径是这样的,
信息输入路径:Client –> Connection -> Server
信息输出路径:Server -> Connection -> Client
和连接相关的几个设置有character_set_client、character_set_connection和character_set_results。比如执行一个sql查询,服务器端会以character_set_client设置的字符集接受这个查询,而后根据转换成character_set_connection设置的字符集,处理完成后,以character_set_results字符集将结果返回。要更改连接时的字符集可以执行命令
SET NAMES 'charset_name';
它等价于执行了下面三个命令
mysql> SET character_set_client = x;
mysql> SET character_set_results = x;
mysql> SET character_set_connection = x;
也不需要每次启动客户端时都进行这几个操作,可以在mysql语句或者选项文件中添加一个--default-character-set=的选项设置。比如你想以gbk格式查询数据库,可以设置此选项为gbk。
2. 如果网站支持多语言,可以选用utf-8编码,如果只在国内使用,则没有必要。如果网站使用了utf-8编码,则数据库,以及网页提交的数据都要使用utf8。
说没有必要,是从各种编码占有的字节长度的考虑,utf-8编码文件比gb2312编码文件大。如果default-character-set=gbk,就不能存utf-8编码的字符,否则会提示“Data too long for column ‘xx’at row”的错误。如果数据量大,才有gbk会节省空间。Latin1和gbk类似。
也正因为如此,如果网页使用了utf-8编码,就要保证所有的编码一致,即数据库也要用utf8编码,utf-8编码提交的网页也要是utf8的。
以下是一些用于查询和修改字符集的mysql命令。
1. 查看数据库支持的所有字符集
mysql>show character set;
或
mysql>show char set;
2. 查看当前的字符集设置
mysql>status;
或
mysql>\s;
3. 查看系统字符集设置
mysql>show variables like 'char%';
4. 修改服务器级设置
a. 临时更改:
mysql>SET GLOBAL character_set_server=utf8;
b. 永久更改:
修改my.ini文件
[mysqld]
default-character-set=utf8
5. 修改数据库级
a. 临时更改:
mysql>SET GLOBAL character_set_database=utf8;
b. 永久更改
改了服务器级就可以了
6. 修改表级
mysql>ALTER TABLE table_name DEFAULT CHARSET utf8;
改后永久生效
7. 修改列级
mysql>alter table `products` change `products_model` `products_model` varchar( 20 ) character set utf8 collate utf8_general_ci null default null;
更改后永久生效
8. 更改连接字符集
a. 临时更改:
mysql> set names utf8;
b. 永久更改:
修改my.ini文件
在[client]中增加:
default-character-set=utf8
最后谈严格模式:Mysql尽可能采用严格模式,一定程序上防止一些编码问题和长度问题(https://blog.csdn.net/fdipzone/article/details/50616247)。
set global sql_mode=''; #这种方法修改,当前会话失效后,这个修改就没意义了,建议修改配置文件的方式修改,修改后,不需要重启mysql服务,立即生效,检查是否生效,执行sql ,select @@sql_mode;
show create table t;
CREATE TABLE `t` (
`c` varchar(255) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8
show variables like "%char%";
set names gbk;
mysql> select c ,hex(c) from t;
+------+--------+
| c | hex(c) |
+------+--------+
| 东 | E4B89C |
| 东 | E4B89C |
| 东 | E4B89C |
| 东 | E4B89C |
| 东 | E4B89C |
| 东 | E4B89C |
| 东 | E4B89C |
| 东 | E4B89C |
+------+--------+
8 rows in set (0.00 sec)
php igbk.php
string(4) "b6ab"
int(1)
string(4) "b6ab"
string(4) "b6ab"
Array
(
[0] => Array
(
[c] => 东
[0] => 东
[hex] => E4B89C
[1] => E4B89C
)
说明三个问题:
1)咱们在显示一个字符时,在SecureCRT里配置的character encoding只是显示,并不像Mysql的set names utf8里的三个参数(character_set_client character_set_connection character_set_results )有由一个字符转向另一个字符集的功效,要想显示正常,则只需要对转出来的结果进行匹配即可显示正常。bin2hex("东")
三条:
一)如果character_set_client和character_set_results 一般情况下要一致,因为一个表示客户端发送的数据格式,另一个表示客户端接受的数据格式为了避免造成数据丢失,需让 character_set_connection的字符编码 大于 character_set_client的字符编码.(MySQL Server收到请求时将请求数据从character_set_client转换为character_set_connection;)
二)对于Mysql旧版本的默认latin情况,里面尽管传入了UTF8,也能正常的显示和插入(character_set_client| latin1(中文你utf8是3个字节,也就是来自前端网页的UTF8的你字,而latin1是按照单个字节解析的,虽然进行了转换,但不会导致二进制内容的变化,但实际上mysql客户端认为我输入了3个latin1字符;还好是UTF8前端,如果客户端是GBK,直接收到一个3字节的UTF8的编码会为是1个gbk字符和一个西欧字符) -> character_set_connection | latin1->),但本质上是以UTF8的方式存入磁盘时是6个字符( character_set_database | utf8 ),而出来时又被Latin1转回为3个字符后被UTF8看成1个汉字也就是你字,而如果这个Lation1不这样6字节变3字节,也就是(character_set_results 的值由latin1变UTF8后)UTF8后,则6个字符以UTF8直接穿透到显示层了,会在SecureCRT(设置UTF8输出显示)看到UTF8是6个字符认为是两个汉字,也就成了我们认为的乱码!!!归结于:由于latin1 是一种非常宽松的编码方式,任何一种编码方式得到的文本,用 latin1 进行解码,都不会发生解码失败——当然,解码得到的结果自然也就是理所当然的“乱码”。
实践如下,实践证明输出与character_set_results的编码有关,当set character_set_results是latin1时会转为输入时的UTF8的三个字符,而set character_set_results="utf8"时,则它输出了存入Mysqld数据库的6个字节的UTF8直接输出,前端认为是两个字也就显示出来了东,也就是把C3A4C2B8C593以SecureCRT配置的前端输出经过UTF8解码显示,就是乱码了。
这个在Mysql里可通过设置set names latin1;并且secureCRT的Character encoding设置为utf8就能正常显示,本质上:
mysql> select c ,hex(c) from t;
+--------+--------------+
| c | hex(c) |
+--------+--------------+
| 东 (这里面的输出是将 hex(c)里的内容C3A4C2B8C593 经过character_set_results=latin1编码后显示,这个显示正常不正常是由SecureCRT的输出时是GB2312,还是UTF8,这儿输出本来是latin1,但是被SecureCRT按utf8显示后就是 三个字节的东字!!! ) | C3A4C2B8C593 |
+--------+--------------+
核心内容:输出的那个Lation1变UTF8(set character_set_results=utf8)后,反而乱码的原因是如下:
From:https://edu.aliyun.com/a/15790
基础学习:关于前面提到的字节和位的一个关系的一个基础知识:位:"位(bit)"是电子计算机中最小的数据单位。每一位的状态只能是0或1。字节:8个二进制位构成1个"字节(Byte)",它是存储空间的基本计量单位。1个字节可以储存1个英文字母或者半个汉字,换句话说,1个汉字占据2个字节的存储空间。即1个字节等于8个比特(1Byte=8bit)。八位二进制数最小为00000000,最大为11111111(0XFF,全是1,也就是两个F(十进制是255 ),C3的对应的十进制195,A4对应十进制是164);通常1个字节可以存入一个ASCII码,“c3a4 c2bd c2a0”(6个字节) ,ASCII码用十六进制表示的,C3、a4等等,http://ascii.911cha.com/。
三)对于2的这种情况是在升级时可全部改为UTF8的编码的,是可以根据 mysqldump 时加上 --default-character-set=latin1,导出正常的Utf8编码的中文且显示正常的字符,但是加了这 个后,记得修改里面的set names latin1,为set names utf8, 而在程序代码里连接时也加上set names utf8,黄冶是default-character-set=utf8就能正常显示。(mysqldump -h 127.0.0.1 -P 3306 -u username --default-character-set=latin1 -p databasename > dumpfile.txt)
=========================================================================================
[转]深入Mysql字符集设置,比阿里巴巴技术团队的更形像,有图:
https://blog.csdn.net/haiross/article/details/51273630 (请道因为UTF8插入时如果存的是比它小的,可能转时出现问题,而没法恢复,因为存入时就坏了:插入数据将经过utf8=>utf8=>latin1的字符集转换,若原始数据中含有/u0000~/u00ff范围以外的Unicode字符,会因为无法在latin1字符集中表示而被转换为“?”(0x3F)符号,以后查询时不管连接字符集设置如何都无法恢复其内容了。而以latin1入到Utf8后,会导致字符数变多,也就是存储空间会因为中文的UTF8是3个及以上(文章里提到的3个字节变成6个字节),占用了更多磁盘,插入操作的数据将经过latin1(character_set_client)=>latin1(character_set_connection)=>utf8(character_set_results)的字符集转换过程,这一过程中每个插入的汉字都会从原始的3个字节变成6个字节保存; )
阿里云大学谈到了其乱码的本质一样的用你的UTF8作描述的文字版:https://edu.aliyun.com/a/15790
MySQL的几个character_set变量的说明:https://www.linuxidc.com/Linux/2017-04/143263.htm
MySQL抓包工具:MySQL Sniffer【转】:
https://www.cnblogs.com/zhoujinyi/p/6497231.html
利用tcpdump对mysql进行抓包操作技巧_Mysql:
https://edu.aliyun.com/a/15214
特殊变量之character_set_system:character_set_system是个只读数据不能更改。也没多少改的意义,他是元数据的编码,相信不会有人用中文做数据库名和字段名之类的吧,这个字段和具体存储的数据无关。
mysql> show variables like '%char%';
+--------------------------+-----------------------------------------------------+
| Variable_name | 说明 |
+--------------------------+-----------------------------------------------------+
| character_set_client | 客户端字符集 |
| character_set_connection | 当前连接字符集 |
| character_set_database | 数据库字符集 |
| character_set_filesystem | 文件系统字符集,不要修改,使用binary即可 |
| character_set_results | 返回结果集字符集 |
| character_set_server | 服务器默认字符集,当数据库、表、列没有设置时,如Show create table里的中文表字段批注 |
| | 默认使用此字符集 |
| character_set_system | 固定为utf8 |
+--------------------------+-----------------------------------------------------+
| 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 |
------------------------而latin1是按照单个字节解析的,虽然进行了转换,但不会导致二进制内容的变化!-------------------------------------------
这里首先需要解释的是,我想应该就是连接通道的含义了。那什么是连接通道呢?
背景:这种理解其实是早期程序员以PHP为例通常觉得数据存为简体中文比教少占空间,utf8中文占用大于等于三,由unicode字符集决定的,其实现在看来用utf8都不是啥大事,磁盘很便宜。再就是早期mysql默认就是拉丁字符集合。
导致出现想转为utf8的,首先导出时候得指定字符集为拉丁,假如不加指定以拉丁导出其默认是utf8,往往那个建表结构的comment注释是utf8,出现评论正常,数据库内容中文乱码,用file xxx.sql检查的确是一个utf8的sql文件。
如果MySQL服务器上的字符集是 latin1 的,mysqldump 出来的中文都是乱码!一个简单的办法是加上默认字符集选项,如:
mysqldump -h 127.0.0.1 -P 3306 -u username --default-character-set=latin1 -p databasename > dumpfile.txt #character_set_connection、character_set_client、character_set_results三个字符集一次性被设置为latin1了。character_set_client、character_set_connection、character_set_results这3个参数值是由客户端每次连接进来设置的,和服务器端没关系。我们登陆进mysql后,执行 set names utf8; 实际就是同时修改上面的这3个参数值的。From:MySQL的几个character_set变量的说明,https://www.linuxidc.com/Linux/2017-04/143263.htm。
有些网站在开发时,并没有正确指定编码,採用了预设的 latin1 ,并且将 big5 的中文资料直接储存。这种情况如果直接用 mysqldump 直接匯出,结果可能是一堆乱码,必须加上 --default-character-set=latin1 让匯出的资料保持正常的 big5 编码。
mysqldump -u USER -p DBNAME --default-character-set=latin1 > DBNAME.sql
如果在匯出备份资料时,忘记加上参数,產生的乱码资料,用 iconv 可以救回部分(以下指令示范假设匯出档為错误编码的utf8),参数 "-c" 表示忽略无法转码的字元。
iconv -c -f utf8 -t latin1 DBNAME.sql > DBNAME.big5.sql
对于 big5 编码的匯出档,也可以用 iconv 转换成 utf8 格式。
iconv -c -f big5 -t utf8 DBNAME.sql > DBNAME.utf8.sql
再进行编码格式设定的替换。
perl -pi -e 's/big5/utf8/g' DBNAME.utf8.sql
旧的Mysql默认选项:
mysql> show variables like "%set%";
+--------------------------+----------------------------+
| Variable_name | Value |
+--------------------------+----------------------------+
| character_set_client | latin1 |
| character_set_connection | latin1 |
| character_set_database | latin1 |
| character_set_results | latin1 |
从源码编译mysql的时候,通过编译参数进行设置
编译的时候如果指定了-DDEFAULT_CHARSET和-DDEFAULT_COLLATION,那么:
创建database、table时,会将其作为默认的字符集/字符序。
client连接server时,会将其作为默认的字符集/字符序。(不用单独SET NAMES)
shell> cmake . -DDEFAULT_CHARSET=utf8 \
-DDEFAULT_COLLATION=utf8_general_ci
RPM打包:
cmake . \
...
-DDEFAULT_CHARSET=utf8 \
...
make %{?_smp_mflags}
=============================================================================
搞成utf8的编码,怎么办?分三步:
1)mysqldump default-character-set=latin databse table > /tmp/utf8format.sql #本质上是存在数据库里面的编码就是latin,指定以Latin字符编码给导出到一个utf8文件里面。
2)在导出文件里面因为1加入了指定导出编码了,于是文件里面sql语句有一个set names latin,把这个改成set names utf8。此时用less看无论建表结构的评论注释还是表里内容均显示正常,中文不乱码。
3)在导入数据的mysqlserver的my.cnf设置尽可能全utf8,如果不设置因为2在sql文件建立表和插入数据前加入设置编码了,直接mysql导入即可。保证全是utf8后,PHP的连接字符串编码也是UTF8即可。
所谓连接通道,就是客户端和服务器端保持连接的一个通道,它是逻辑上的一个概念。客户端通过连接通道发送sql语句到服务器端,服务端执行,将结果再通过连接通道返回至客户端。the connection is the pass when you connect to the server.
这个过程中,有几个临界点(逻辑上概念),是我们需要注意的,mysql也就在这几个临界点上做了文章。
1、当语句离开客户端的时候:
从客户端出来的,包括sql语句本身(这里里面就包含字符串和关键字等了),以及character_set_client系统变量。为什么要包含这个变量呢?这个变量的作用说明2点,也是它的作用:一是表示该语句中的字符集是使用character_set_client指定的字符集编码的,二是通过此系统变量来告诉服务器所发送来的语句中的字符集编码。
2、当服务器端接受到客户端的语句的时候:
mysql会使用character_set_connection/collation_connection指定的字符集以及校验规则,将客户端的字符串,做一个从character_set_client到character_set_connection的转换。
3、当服务器处理好结果以后,在把结果传给客户端前:
mysql会先将结果转换成character_set_results指定的字符集,然后传回给客户端。
当字符串在mysql服务器的时候,最终以什么格式存储到mysql数据库中,这个是受到具体的数据表级别、列级别字符集设置的控制了。
从上面的介绍中,我们就知道和连接通道相关几个参数了,他们分别是character_set_client/connection/results,可以如下查看:
mysql> show variables like 'char%';
+--------------------------+-------------------+
| Variable_name | Value |
+--------------------------+-------------------+
| character_set_client | latin1 |
| character_set_connection | latin1 |
| character_set_database | gbk |
| character_set_results | latin1 |
| .........................| ...... |
+--------------------------+-------------------+
8 rows in set (0.00 sec)
mysql> show variables like 'colla%';
+----------------------+-------------------+
| Variable_name | Value |
+----------------------+-------------------+
| collation_connection | latin1_swedish_ci |
| collation_database | gbk_bin |
| collation_server | latin1_swedish_ci |
+----------------------+-------------------+
3 rows in set (0.00 sec)
下面我们来做个实验,来证明一下这个结果:
首先保证character_set_connection与character_set_results以及底层存储字符集的一致性,看看character_set_client的效果。
mysql> show variables like 'char%';
+--------------------------+------------------+
| Variable_name | Value |
+--------------------------+------------------+
| character_set_client | latin1 |
| character_set_connection | gbk |
| character_set_database | gbk |
| character_set_results | gbk |
| .........................| ...... |
+--------------------------+------------------+
8 rows in set (0.00 sec)
mysql> create table t (a varchar(10)); -- 这里没有指定字符集,就默认使用了database的字符集gbk了
Query OK, 0 rows affected (0.08 sec)
mysql> insert into t values('中国');
Query OK, 1 row affected (0.00 sec)
mysql> select * from t;
+-------+
| a |
+-------+
| ???ú |
+-------+
1 row in set (0.00 sec)
由此可以看到由于latin1与gbk对汉字的编码方式不一样(或者说latin1根本就不能正确编码汉字),在这个collection过程中,从character_set_client到character_set_connection转换时,就把你输入的好好的汉字转换成乱码了。那么,如果让过程不发生转换呢?
mysql> set character_set_client=gbk;
Query OK, 0 rows affected (0.00 sec)
mysql> insert into t values('人民');
Query OK, 1 row affected (0.02 sec)
mysql> select * from t;
+-------+
| a |
+-------+
| ???ú |
| 人民 |
+-------+
2 rows in set (0.00 sec)
可见,这里是能正确存储和显示的,很简单,因为任何转换都没有发生,当然就不会出现乱码了。
接着做实验,我们让character_set_connection发生变化:
mysql> set character_set_connection=latin1;
Query OK, 0 rows affected (0.00 sec)
mysql> insert into t values ('共和国');
Query OK, 1 row affected (0.00 sec)
mysql> select * from t;
+-------+
| a |
+-------+
| ???ú |
| 人民 |
| ??? |
+-------+
3 rows in set (0.00 sec)
可见,这里character_set_client=gbk,character_set_connection=latin1,character_set_database=gbk。首先,client到connection过程中,汉字被转换成乱码(这个过程可能就会丢失信息);然后存储数据的时候,又从connection到存储的字符集(gbk)发生一次转换,乱码被转换成“更”乱码。这里如果connection与client的字符集有种包容性关系的话,如character_set_client=gbk, character_set_connection=utf8,character_set_results=gbk,底层存储也是gbk编码,由于utf8“兼容”所有的字符集,故在转换过程中不会发生信息丢失,查询的时候也不会是乱码,如下:
mysql> show variables like 'char%';
+--------------------------+-----------------+
| Variable_name | Value |
+--------------------------+-----------------+
| character_set_client | gbk |
| character_set_connection | utf8 |
| character_set_database | gbk |
| character_set_results | gbk |
| .........................| ...... |
+--------------------------+-----------------+
8 rows in set (0.00 sec)
mysql> create table t (a varchar(10)) charset=gbk;
Query OK, 0 rows affected (0.05 sec)
mysql> insert into t values('中国');
Query OK, 1 row affected (0.00 sec)
mysql> select * from t;
+------+
| a |
+------+
| 中国 |
+------+
1 rows in set (0.00 sec)
我来解释一下这个过程,假设“中国”的gbk编码是1234,而utf8的编码是4321,utf8的编码为1234的假设是“淘宝”。client的“中国”的编码1234进入connection,仍然是1234(但实际上它的含义已经发生变化为“淘宝”),存储的时候connection的1234编码到表存储也是1234,刚好正确表达了“中国”的意思,查询返回时,由于results也是gbk,所以不发生转换,正确显示。很显然,如果results又是一种与gbk不兼容的字符集如latin1,查询又会出问题,如下:
mysql> set character_set_results=latin1;
Query OK, 0 rows affected (0.00 sec)
mysql> select * from t;
+------+
| a |
+------+
| ?? |
+------+
1 rows in set (0.00 sec)
当我们改变底层存储的字符集的时候,会怎样?请看如下实验:
mysql> show variables like 'char%';
+--------------------------+---------------+
| Variable_name | Value |
+--------------------------+---------------+
| character_set_client | gbk |
| character_set_connection | gbk |
| character_set_database | gbk |
| character_set_results | gbk |
| .........................| ...... |
+--------------------------+---------------+
8 rows in set (0.01 sec)
mysql> create table t (a varchar(10)) charset=latin1;
Query OK, 0 rows affected (0.05 sec)
mysql> insert into t values('淘宝');
Query OK, 1 row affected, 1 warning (0.02 sec)
mysql> select * from t;
+------+
| a |
+------+
| ?? |
+------+
1 row in set (0.00 sec)
很显然,由于latin1字符集无法存储汉字,故出现乱码。
下面,我们只改变results字符集,看看效果如何:
mysql> show variables like 'char%';
+--------------------------+----------------+
| Variable_name | Value |
+--------------------------+----------------+
| character_set_client | gbk |
| character_set_connection | gbk |
| character_set_database | gbk |
| character_set_results | latin1 |
| .........................| ...... |
+--------------------------+----------------+
8 rows in set (0.00 sec)
mysql> create table t (a varchar(10)) charset=gbk;
Query OK, 0 rows affected (0.08 sec)
mysql> insert into t values('淘宝');
Query OK, 1 row affected (0.00 sec)
mysql> select * from t;
+------+
| a |
+------+
| ?? |
+------+
1 row in set (0.00 sec)
很显然,底层为gbk编码的字符串转换成latin1的字符集的字符,变成乱码,传给客户端,再次转换成gbk字符集,变成“更”乱码,故显示乱码。
下面,我再介绍几个“简约”命令。
SET NAMES ‘x’,(SET NAMES ‘charset_name’ COLLATE ‘collation_name’)相当于:
SET character_set_client = x;
SET character_set_results = x;
SET character_set_connection = x;
实验如下:
mysql> show variables like 'char%';
+--------------------------+----------------+
| Variable_name | Value |
+--------------------------+----------------+
| character_set_client | gbk |
| character_set_connection | gbk |
| character_set_results | latin1 |
| .........................| ...... |
+--------------------------+----------------+
8 rows in set (0.00 sec)
mysql> set names 'ascii';
Query OK, 0 rows affected (0.00 sec)
mysql> show variables like 'char%';
+--------------------------+----------------+
| Variable_name | Value |
+--------------------------+----------------+
| character_set_client | ascii |
| character_set_connection | ascii |
| character_set_results | ascii |
| .........................| ...... |
+--------------------------+----------------+
SET CHARACTER SET ‘x’,相当于:
SET character_set_client = x;
SET character_set_results = x;
SET collation_connection = @@collation_database;
这里collation_connection = @@collation_database的意思就是把collation_connection的设置的collation_database的值,由于collation肯定能确定character set,故其又相当于多做了个设置:把character_set_connetion设置成character_set_database的值。请看如下实验:
mysql> show variables like 'char%';
+--------------------------+-------------+
| Variable_name | Value |
+--------------------------+-------------+
| character_set_client | latin1 |
| character_set_connection | latin1 |
| character_set_database | gbk |
| character_set_results | latin1 |
| ....................... | ...... |
+--------------------------+-------------+
8 rows in set (0.00 sec)
mysql> show variables like 'collat%';
+----------------------+-------------------+
| Variable_name | Value |
+----------------------+-------------------+
| collation_connection | latin1_swedish_ci |
| collation_database | gbk_bin |
| collation_server | latin1_swedish_ci |
+----------------------+-------------------+
3 rows in set (0.00 sec)
mysql> set character set 'ascii';
Query OK, 0 rows affected (0.00 sec)
mysql> show variables like 'char%';
+--------------------------+-------------+
| Variable_name | Value |
+--------------------------+-------------+
| character_set_client | ascii |
| character_set_connection | gbk |
| character_set_database | gbk |
| character_set_results | ascii |
| ....................... | ...... |
+--------------------------+-------------+
8 rows in set (0.00 sec)
mysql> show variables like 'collat%';
+----------------------+-------------------+
| Variable_name | Value |
+----------------------+-------------------+
| collation_connection | gbk_bin |
| collation_database | gbk_bin |
| collation_server | latin1_swedish_ci |
+----------------------+-------------------+
3 rows in set (0.00 sec)
通过以上的介绍,所以为了防止出现乱码,我们可以把character_set_client,character_set_connection,character_set_database,character_set_results设置成同样的值,把collation_connection和collation_database也设置成同样的值,这样就“一劳永逸”了。程序连接mysql的时候,一般都会显示设置character_set_client的值,如java连接中,一般都有如下的一段代码来显示设置这个值:
jdbc:mysql://10.1.6.174:3306/notify?connectTimeout=1000&characterEncoding=utf8
转:
http://rdc.taobao.com/blog/dba/html/184_mysql_connect_charset_collation.html
作者:叶金荣(Email:imysql@gmail.com),来源:http://imysql.cn,转载请注明作者和出处,并且不能用于商业用途,违者必究。
首先,这片文章纯粹是我的个人经验之谈,适用于我常见的环境及项目中.
个人建议,数据库字符集尽量使用utf8(HTML页面对应的是utf-8),以使你的数据能很顺利的实现迁移,因为utf8字符集是目前最适合于实现多种不同字符集之间的转换的字符集,尽管你在命令行工具上可能无法正确查看数据库中的内容,我依然强烈建议使用utf8作为默认字符集.
接下来是完整的一个例子:
1.创建数据库表
mysql>CREATE DATABASE IF NOT EXISTS my_db default charset utf8 COLLATE utf8_general_ci;
#注意后面这句话 "COLLATE utf8_general_ci",大致意思是在排序时根据utf8校验集来排序
#那么在这个数据库下创建的所有数据表的默认字符集都会是utf8了
mysql>create table my_table (name varchar(20) not null default '')engine=MyISAM default charset utf8 default charset utf8;
#这句话就是创建一个表了,制定默认字符集为utf8
2.写数据
例子1是通过php直接插入数据:
a.php
<?php
mysql_connect('localhost','user','password');
mysql_select_db('my_db');
//请注意,这步很关键,如果没有这步,所有的数据读写都会不正确的
//它的作用是设置本次数据库联接过程中,数据传输的默认字符集
//其他编程语言/接口也类似,例如 .net/c#/odbc
//jdbc则设置连接字符串为类似"jdbc:mysql://localhost/db?user=user&password=123456&useUnicode=true&characterEncoding=UTF-8"
mysql_query("set names utf8;");
//必须将gb2312(本地编码)转换成utf-8,也可以使用iconv()函数
mysql_query(mb_convet_encoding("insert into my_table values('测试');", "utf-8", "gb2312"));
?>
例子是通过页面提交插入数据2:
b.php
<?php
//输出本页编码为utf-8
header("content-type:text/html; charset=utf-8");
mysql_connect('localhost','user','password');
mysql_select_db('my_db');
if(isset($_REQUEST['name'))
{
//由于上面已经指定本页字符集为utf-8了,因此无需转换编码
mysql_query(sprintf("insert into my_table values('%s');", $_REQUEST['name']));
}
$q = mysql_query("select * from my_table");
while($r = mysql_fetch_row($q))
{
print_r($r);
}
?>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<form action="" method="post">
<input type="text" name="name" value="">
<input type="submit" value='submit'>
</form>
自此,使用utf8字符集的完整的例子结束了.
如果你想使用gb2312编码,那么建议你使用latin1作为数据表的默认字符集,这样就能直接用中文在命令行工具中插入数据,并且可以直接显示出来.而不要使用gb2312或者gbk等字符集,如果担心查询排序等问题,可以使用binary属性约束,例如:
create table my_table ( name varchar(20) binary not null default '')engine=MyISAM default charset utf8 default charset latin1;
附1:旧数据升级办法
以原来的字符集为latin1为例,升级成为utf8的字符集。原来的表: old_table (default charset=latin1),新表:new_table(default charset=utf8)。
第一步:导出旧数据
mysqldump --default-character-set=latin1 -hlocalhost -uroot -B my_db --tables old_table > old.sql
第二步:转换编码(类似unix/linux环境下)
iconv -t utf-8 -f gb2312 -c old.sql > new.sql
或者可以去掉 -f 参数,让iconv自动判断原来的字符集
iconv -t utf-8 -c old.sql > new.sql
在这里,假定原来的数据默认是gb2312编码。
第三步:导入
修改old.sql,在插入/更新语句开始之前,增加一条sql语句: "SET NAMES utf8;",保存。
mysql -hlocalhost -uroot my_db < new.sql
大功告成!!
附2:支持查看utf8字符集的MySQL客户端有
1.) MySQL-Front,据说这个项目已经被MySQL AB勒令停止了,不知为何,如果国内还有不少破解版可以下载(不代表我推荐使用破解版 :-P)。
2.) Navicat,另一款非常不错的MySQL客户端,汉化版刚出来,还邀请我试用过,总的来说还是不错的,不过也需要付费。
3.) PhpMyAdmin,开源的php项目,非常好。
4.) Linux下的终端工具(Linux terminal),把终端的字符集设置为utf8,连接到MySQL之后,执行 SET NAMES UTF8; 也能读写utf8数据了。
附3:直接使用MySQL提供的 ALTER 语法转换字符集
这对广大非utf8又想转成utf8的用户来说,是个天大的喜讯,我也是在学习MySQL手册是才发现的。具体用法如下:
ALTER TABLE OLD_TABLE CONVERT TO CHARACTER SET charset_name [COLLATE collation_name];
转换之前,记得要先备份旧表,以防万一。更多详情请查看相关版本的MySQL手册。下面是一个实际的例子:
ALTER TABLE `t_yejr` CONVERT TO CHARACTER SET UTF8;
这个方法应该是从MySQL 4.1才开始提供的,大家可以检查一下自己的版本是否支持,如果不支持,只好按照上面提到的转换了。enjoy it!!!
http://www.sun023.com/post/108.html
再摘录自同事的一点更简洁明了的看法:
在web开发中,经常会遇到中文显示为乱码的情况。这里简单总结一下在遇到这种问题时分析和解决的思路。
1. 不论数据库是何种编码,只要在取出数据时设置的编码和存入数据时设置的编码一致,就不会出现乱码现象。
Mysql数据字符集分成四个级别:服务器级、数据库级、表级和连接级。通过status命令可以看到自己的mysql的这些设置。前三者主要控制数据在服务器端的存储。这些数据集的设置有从上至下的包含关系,也就是说,如果表没有做特设的字符集设置,则会继承数据库级的设置。而连接级字符集主要是用于客户端和服务器端的数据交互。
执行sql语句时数据的流转路径是这样的,
信息输入路径:Client –> Connection -> Server
信息输出路径:Server -> Connection -> Client
和连接相关的几个设置有character_set_client、character_set_connection和character_set_results。比如执行一个sql查询,服务器端会以character_set_client设置的字符集接受这个查询,而后根据转换成character_set_connection设置的字符集,处理完成后,以character_set_results字符集将结果返回。要更改连接时的字符集可以执行命令
SET NAMES 'charset_name';
它等价于执行了下面三个命令
mysql> SET character_set_client = x;
mysql> SET character_set_results = x;
mysql> SET character_set_connection = x;
也不需要每次启动客户端时都进行这几个操作,可以在mysql语句或者选项文件中添加一个--default-character-set=的选项设置。比如你想以gbk格式查询数据库,可以设置此选项为gbk。
2. 如果网站支持多语言,可以选用utf-8编码,如果只在国内使用,则没有必要。如果网站使用了utf-8编码,则数据库,以及网页提交的数据都要使用utf8。
说没有必要,是从各种编码占有的字节长度的考虑,utf-8编码文件比gb2312编码文件大。如果default-character-set=gbk,就不能存utf-8编码的字符,否则会提示“Data too long for column ‘xx’at row”的错误。如果数据量大,才有gbk会节省空间。Latin1和gbk类似。
也正因为如此,如果网页使用了utf-8编码,就要保证所有的编码一致,即数据库也要用utf8编码,utf-8编码提交的网页也要是utf8的。
以下是一些用于查询和修改字符集的mysql命令。
1. 查看数据库支持的所有字符集
mysql>show character set;
或
mysql>show char set;
2. 查看当前的字符集设置
mysql>status;
或
mysql>\s;
3. 查看系统字符集设置
mysql>show variables like 'char%';
4. 修改服务器级设置
a. 临时更改:
mysql>SET GLOBAL character_set_server=utf8;
b. 永久更改:
修改my.ini文件
[mysqld]
default-character-set=utf8
5. 修改数据库级
a. 临时更改:
mysql>SET GLOBAL character_set_database=utf8;
b. 永久更改
改了服务器级就可以了
6. 修改表级
mysql>ALTER TABLE table_name DEFAULT CHARSET utf8;
改后永久生效
7. 修改列级
mysql>alter table `products` change `products_model` `products_model` varchar( 20 ) character set utf8 collate utf8_general_ci null default null;
更改后永久生效
8. 更改连接字符集
a. 临时更改:
mysql> set names utf8;
b. 永久更改:
修改my.ini文件
在[client]中增加:
default-character-set=utf8
作者:jackxiang@向东博客 专注WEB应用 构架之美 --- 构架之美,在于尽态极妍 | 应用之美,在于药到病除
地址:https://jackxiang.com/post/1387/
版权所有。转载时必须以链接形式注明作者和原始出处及本声明!
最后编辑: jackxiang 编辑于2018-4-24 17:38
评论列表