编码问题
计算机的世界里只有 0 和 1,而对于人类有图片声音文字等,那么计算机如何存储这些图片文字等信息呢,对于 01000001,如果要看成数字,那就是 65,如果要看成字符,那就是 'A',对于这样的规则,就是编码问题。
字符集
ASCII字符集(最古老的字符集)
ASCII 字符集最多只有 127 个字符,所以只需要 1 个字节,而且该字节的最高位为 0 就可以表示这么多个字符了。
GB2312编码字符集
如果使用一个字节,那么对于汉字,有那么多,不可能足够表示,因此 GB2312 使用 2 个字节。而且 GB2312 使用的两个字节不占用 0-127,因为防止与 ASCII 码冲突,试想:如果使用到 0-127,那么 [65][65] 到底表示 AA,还是中文呢?这样 gb2312 能够表示的汉字数就减少了,实际上 gb2312 只收录了 6763 个汉字。
GBK编码字符集
GBK 编码可以表示更多的汉字,想一下:GBK 相比于 GB2312,如何才能表示多一点的汉字呢?其实就是 GBK 将第二个字节的 0-127 也使用起来了,这并不会造成冲突,因为只要第一个字节是 128-255 范围,就继续往后再取一个字节,作为汉字编码的一部分。实际上 GBK 编码的第一个字节表示的范围是 0x81-0xFE,第二个字节的范围是 0x40-0x7E,0x80-0xFF。所以GBK 收录的汉字是 21003 个。
其他字符集编码
日本使用 JIS 字符集,经常见到的 ANSI 代表的是本地字符集(本地字符集会根据操作系统的类型去给定不同的字符集,如简体中文下就是 GB2312,日文下就是 JIS),解决了不同国家不同字符集的问题就引来了一个新的问题,不同国家的字符集怎么兼容?中文下的中华人民共和国并不会在日本字符集下显示为正常的日文的中华人民共和国。
国际化字符集
unicode 是一种在世界通用的码表,它为每种语言的每个字符都设定的统一并且唯一的二进制编码。unicode 采用 4 个字节存放字符,可以表示 40 多亿个字符,但是常用的还是集中在前 2 个字节里。
注意:unicode 只负责编码,不负责传输。
unicode与utf8的关系
相当于原始数据与压缩数据的关系,通过 unicode 可以唯一推测 utf8,根据 utf8 也可以反推 unicode。
utf8 的长度是变长的,unicode 与 utf8 的转换关系如上,如果 unicode 编码在 0x00000000-0x0000007F 之间的,转换出来只有一个字节,如果是 0x00000080-0x000007FF 之间的,转换出来有两个字节,同理如上表。那么就有个问题,既然 utf8 是变长编码,那么如何确定每个字符的字节个数呢,如果使用了 utf8 编码,又有中文又有英文,怎么截取字符的边界呢?
仔细观察上图发现,如果遇到的该字节最高位为 0,那么就是一个字节,按一个字节转换,如果遇到的这个字节最高位是 110,那么就有两个字节,如果是 1110,那么就是三个字节,同理一直到 6 个字节。
问题:乱码是如何形成的?
回答:第一种可能是实际编码与解析编码不一致,比如 utf8 编码的文件你非要用 gb2312 去解析,必然会乱,第二种可能是传输的时候编码不一致,导致传输时字节丢失,如 utf8 编码的文件,你非要转为 gb2312 然后去传输,由于 utf8 容量大,gb2312容量小,某些字符不兼容就被丢弃,这种情况是不可恢复的。
MySQL字符集参数详解
客户端,连接器,服务端
MySQL 中的字符编码通常涉及三个端:客户端,连接器,服务端(客户端的编码字符需要经过连接器,再存储到服务端),如果服务器的数据库某张表是 utf8 编码的,而 MySQL 客户端是 gbk 编码的,那么以 gbk 的编码往数据库插入数据,怎么才能做到不乱码呢?
客户端的字符编码是 gbk 的,连接器的字符编码是 utf8 的,客户端要把数据存进数据库时先在连接器处把 gbk 转为 utf8,由于数据库是 utf8 编码的,此时不需要再进行转换就可以将数据送进数据库里。
如果连接器字符编码跟客户端是一样的,那么客户端到连接器是不需要进行字符编码转换的,而连接器跟数据库的编码不一致,此时就需要经过编码转换。
设置客户端,连接器及返回时的编码
1.告诉客户端使用的编码是 gbk,set character_set_client=gbk;
2.告诉连接器使用 utf8 编码,set character_set_connection=utf8;
3.告诉返回结果是 gbk 编码的,set character_set_results=gbk;
注意:XShell 可以在"文件->属性->终端->编码"下查看输入的时候默认是什么编码的,根据这个编码去判断客户端以及返回时该用什么编码。
案例分析
1.如果客户端是 gbk 编码,而连接器是 latin1 编码,数据库是 utf8 编码,那么此时插入数据后查看是否乱码?
回答:是乱码的,因为在 gbk 编码转 latin1 编码时出现了丢失,这种丢失造成的乱码是不可修复的,所以数据库编码要大于连接器编码,连接器编码要大于客户端编码。
2.如果将客户端,连接器和返回结果都设置成 gbk 编码,此时可以直接使用 set names gbk,相当于 set character_set_client=gbk;set character_set_connection=gbk;set character_set_results=gbk;