UTF8编码字节流错误小析

      对于对称加密,首先生成字节流形式的key与iv,之后对指定字符串进行加密,然后将key与iv转换为字符串传输到另一端,另一端再将字符串转换回字节流完成解密工作.在key与iv的字节流与字符串之间的转换中如果选用Utf8编码,则可能会让转换回的二进制流与之前的字节流不一致.代码如下:

byte[] bytes = new byte[]{146, 174, 27, 74, 223, 159, 52, 180};
string s = Encoding.UTF8.GetString(bytes);
Console.WriteLine(s);
byte[] bytes2 = Encoding.UTF8.GetBytes(s);
Console.WriteLine(bytes2);

      你会发现,bytes2的内容变为了:{239,191,189,239,191,189,27,74,223,159,52,239,191,189}.

      从表面看,其将146, 174, 180三个数扩展成了 239,191,189.但毫无规律可寻,223比它们都大,保持了下来,27之类的比他们小,也保持了下来,甚至是159,在它们中间,也保存了下来.说双数变吧,74是双数,却也保持了下来.后来经过不懈努力,终于大致弄懂原因了.

      1.什么是字符编码.

      字符编码,就是将计算机里记录的0与1以一定的规则转换成人类的字符,如英文或中文,是计算机表达字符的方式,其本质就是一个翻译,.目前计算机有多种表式方式,比如ASCII之类的.不同的方式对字节基数要求不一样,有的固定字节/字符的,如Ascii是一字节一字符,Gb2312是两字节一字符,有的是不固定字节/字符的,如Utft8,就从一字节到四字节表示一个字符.不同的方式翻译方式也不一样,比如对于字节{84,128,26,144},如果使用Gb2312翻译,就是"联通"两字,使用Utf8翻译,就是乱码"T�□�"

      2.Utf8的特殊性

      字节流的组合有无数种,但能被字符集识别的只是其中的一部分.就像人的嗓子可以发出很多种声音,但只有26个声母与5个韵母的组合才能发音成人类语言.Utf8也不例外.Utf8的编码规则如下:

Unicode编码(16进制) UTF-8 字节流(二进制)
000000 – 00007F 0xxxxxxx
000080 – 0007FF 110xxxxx 10xxxxxx
000800 – 00FFFF 1110xxxx 10xxxxxx 10xxxxxx
010000 – 10FFFF 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx

      可以看到

      1.以0,110,1110,11110开头的字节,可以被识别

      2.如果以10开头,则要求前一个字节必须以10,110,1110,11110开头

      3.如果以10开头且前一个也以10开头,则再前一个必须以10,1110,11110开头

      4.如果以10开头且前一个以10开头且再前一个也以10开头,则再前一个必须以11110开头

      5.不能被识别的字节,每一个会转换成三个字节{11101111,10111111,10111101},也就是239,191,189

      3.解析问题

      上面所提到的字节流所对应的二进制流如下:

146 10010010
174 10101110
27 00011011
74 01001010
223 11011111
159 10011111
52 00110100
180 10110100

      可以看到,27,74,223,52满足规则1,159满足规则2,而146,174,180一个也不满足,各个被转换成了239,191,189.所以,最终的结果就是如上所示了.

      使用Ascii,Utf7, Utf32编码都会有不同程度的变样,理由同Utf8.

      4.正确的做法

      虽然经过我的实验,使用Unicode编码可以完全还原,但将字节流转换成字符串的正确做法是进行Base64编码.不同与其它的语言编码,这是专门用于字节流与节符串转换的编码.使用Convert类的ToBase64String方法与FromBase64String方法即可完成全部功能.Base64的相关知识请自行谷哥.

      参考的文章:

      中文编码杂谈

      base64

原文地址:https://www.cnblogs.com/ljzforever/p/2950308.html