简介:本章节经过情景模拟,和你们一块儿探讨为何编码不一样会出现乱码html
做者:手辨
准备一些须要的工具,方便后面的虚拟场景mysql
密码本A的字符坐标规则:假设一个字符的密文由三部分组成,顺序分别是"U+5600的56"+"表格最上方的坐标"+"表格最左边的坐标",举例"570B"是"國","56E7"是"囧",以下第一个和第二个密码表,这些密文是16进制的格式,下同sql
密码本B的字符坐标规则:假设一个字符的密文由三部分组成,顺序分别是"表格左上角的字符串"+"表格最左边的坐标"+"表格最上方的坐标",举例"87E5"是"囧","87F8"是"國",以下第一个密码表,这些密文也是16进制的格式,下同 数据库
即咱们可能还须要对咱们拿到的密文进行再一次的加密,好比”囧”的密文是” "56E7”按照开头假设的密文规则,这是16进制的数,那咱们还须要把这个16进制的56E7经过必定的规则,再次加密,针对这两个密码本的加密规则假设以下:安全
密码本A密文再次加密规则:session
16进制密文转换为10进制后对应的范围工具
16进制数转换为2进制后对应的加密格式测试
0至127编码
0xxxxxxx加密
128至2047
110xxxxx 10xxxxxx
2048至65535
1110xxxx 10xxxxxx 10xxxxxx
65536至2097151
11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
好比56E7这个密文再次加密,他转换成10进制是22247,22247在”2048-65535”的范围内,那他的加密格式就是1110xxxx10xxxxxx10xxxxxx,暂且称它为466格式,如何加密呢?以下:
首先,0101011011100111--------按照4 6 6的格式拆分后--------->0101 011011 100111
而后,对0101 011011 100111按照466格式进行转换------->11100101 10011011 10100111
把转换后的111001011001101110100111转换为16进制就是e59ba7,e59ba7就是56E7再次加密的最终密文,这样就由原来的"56E7"是"囧"变成" E59BA7"是"囧",甚至能够根据这个加密方式,生成一个新的密码本C
密码本B的再次加密规则:
密码本B的16进制密文再也不进行二次加密,直接使用得到的16进制密文,例如56E7不会与密码本A同样再次加密,直接使用56E7
第二节 漫画情景
模拟一个虚构的场景,场景里的元素是:
今天,小明须要将一个很重要的数据,告诉公司的小丽把这个数据存到小明本身的档案里:
、
小明发现,他获取到的数据是“闃块噷”,而不是3天前存储的”阿里”,若是小明意识到本身的错误,把小红寄给他的数据先转换成密码本B的密文,再用转换的密文对照密码本A查找呢?
第二章 Mysql的乱码
若是上面的场景,放到数据库里,会同样吗?拿mysql做为例子,测试一下,数据从客户端发送到server并存入表中成为数据而后查询该数据的过程,mysql中控制这个过程的编码的参数主要是character_set_client,character_set_connection,character_set_results,表字符集以及程序字符集等,能够看下文档。
第一节 mysql的情景对应
字符集:
unicode gbk
字符编码规则:
utf8:只针对unicode,至关于第一章情景中的二次加密
gbk:不对gbk再编码
参数与情景对应:
程序字符集:小明加密数据时使用的密码本A
character_set_client:小明在寄信时,写错了数据加密方式,写的是密码本B,而不是A
character_set_connection:安全规则,须要转换的加密格式
character_set_results:寄信时须要使用的加密格式
表或者库字符集:小明的档案的加密格式
Server端处理:小丽/小红
第二节 mysql测试
参数设置:
程序字符集:utf8
character_set_client:utf8
character_set_connection:utf8
character_set_results:utf8
表或者库字符集:utf8
Server端处理:mysql program
测试结果:无乱码
参数设置:
程序字符集:utf8
character_set_client:gbk
character_set_connection:utf8
character_set_results:utf8
表或者库字符集:utf8
Server端处理:mysql program
测试结果:只设置set character_set_client=gbk后,查询乱码
结合第一章的场景,为何乱码呢?
程序将"阿里"以utf8编码进行转换发送(unicode转utf8与情景中的规则一致),可是不当心在session中设置了character_set_client=gbk,character_set_connection =utf8,character_set_results=utf8,相似以下现象:
形状:阿里
Utf8方式下的16进制编码值:E998BF E9878C
二进制:11101001 10011000 10111111 11101001 10000111 10001100(utf8将2个汉字转换后6个字节)
到达server端,server经过character_set_client=gbk知道客户端发过来的数据是gbk编码的(实际是utf8编码的,客户端不当心写错了),那server端就按照gbk来解码了,解码后以下:
二进制:11101001 10011000 10111111 11101001 10000111 10001100(程序端的两个汉字通过utf8转换后的二进制数据,一共6字节,可是server端不知道,由于server经过character_set_client=gbk得知,这不是utf8,这是gbk的,我要按照gbk来解码)
gbk转换后的16进制编码值:E998 BFE9 878C(经过这个编码,查找gbk的码表,获取他的形状为:闃块噷)
2.2.3.3 第三步处理
Servre还须要检查character_set_connection参数,发现character_set_connection=utf8,而以前是gbk,那就须要将gbk的这三个字节转换为utf8,怎么转换呢?按照第一章的情景,先转unicode(密码本A),再将获得的unicode再次编码为utf8:
“闃块噷“三个字符的unicode编码是 95c3 5757 5677
转换成二进制:10010101 11000011 01010111 01010111 01010110 01110111(这三个汉字是6个字节)
经过unicode,再转换为utf8的实现方式,实现方式以下:
首先转换95c3( 1001010111000011),他的范围在第三行(3字节)
1001 010111 000011,根据第三行的格式,从低位往高位的顺序,按照格式分红4 6 6 的格式
按照格式填充,填充后:11101001 10010111 10000011,填充后转换成16进制是e99783,则”闃”在utf8中就是”e99783”,一样,”块”在utf8中就是” e59d97”,” 噷”在utf8中就是” e599b7”, 因而转换后的结果就是:
编码:E99783 E59D97 E599B7
二进制:111010011001011110000011111001011001110110010111111010000000000000000000
server端处理好以后,继续向下走,准备存储数据了,检查了存储的表是utf8的,不须要转换了,直接存储
编码:E99783 E59D97 E599B7
二进制:111010011001011110000011111001011001110110010111111010000000000000000000
程序插入完成后,想查询下以前插入的数据,因而客户端在当前session下执行了select操做,server端收到请求后,去表里找数据,找到了以前写入的数据
111010011001011110000011111001011001110110010111111010000000000000000000
server检查character_set_results=utf8,与表的字符编码是一致的,那就不须要转换了,直接把111010011001011110000011111001011001110110010111111010000000000000000000给程序了,程序收到后,就解码111010011001011110000011111001011001110110010111111010000000000000000000,由于程序自己也是utf8的,不须要转换,因而按照utf8来解码数据,111010011001011110000011111001011001110110010111111010000000000000000000的16进制是E99783E59D97E599B7(符号是闃块噷,乱码了)