为什么不``Encoding.UTF8.GetBytes(Encoding.UTF8.GetString(x))== x`

时间:2012-03-16 15:58:09

标签: c# utf-8

在.NET中为什么不是这样:

Encoding.UTF8.GetBytes(Encoding.UTF8.GetString(x))

返回任意字节数组x的原始字节数组?

回答另一个问题是mentioned,但响应者没有解释原因。

4 个答案:

答案 0 :(得分:3)

首先,正如watbywbarif所提到的,你不应该使用==比较序列,这不起作用。

但即使您正确地比较数组(例如使用SequenceEquals()或只是通过查看它们),它们并不总是相同。可能发生这种情况的一种情况是x是无效的UTF-8编码字符串。

例如,0xFF的1字节序列无效UTF-8。那么Encoding.UTF8.GetString(new byte[] { 0xFF })会返回什么?它是 ,U + FFFD,替换字符。当然,如果你打电话给Encoding.UTF8.GetBytes(),它就不会回复0xFF

答案 1 :(得分:1)

字符编码(特定于UTF8)对于相同的代码点可能具有不同的形式。

因此,当您转换为字符串并返回时,实际字节可能表示不同(规范)形式

另见String.Normalize(NormalizationForm.System.Text.NormalizationForm.FormD)

另见:

  

某些Unicode序列被认为是等效的,因为它们代表相同的字符。例如,以下内容被认为是等效的,因为其中任何一个都可用于表示“ắ”:

"\u1EAF" 
"\u0103\u0301" 
"\u0061\u0306\u0301" 
     

但是,序数(即二进制)比较认为这些序列不同,因为它们包含不同的Unicode代码值。在执行序数比较之前,应用程序必须对这些字符串进行规范化,以将它们分解为基本组件。

该页面附带了一个很好的示例,可以显示哪些编码始终被标准化

答案 2 :(得分:1)

这是因为==不会比较数组的每个元素。它与Encoding.UTF8无关。 检查一下:

var a = new byte[] { 1 };
var b = new byte[] { 1 };
bool res = a == b;

答案 3 :(得分:1)

另一个角度来看,Encoding 设计用于往返数据,但它们设计为往返的数据为{{1}数据,编码为char,而不是相反。这意味着,在所讨论的byte的功能范围内,每个Encoding值都有char值(1或更多)的相应编码,这些值将变回完全相同的byte值。char (值得注意的是,并非所有Encoding都能为所有可能的char值执行此操作 - 例如,Encoding.ASCII只能支持char [0, 128)范围内的值。)

因此,如果您从字符数据开始,并且需要一种方法来存储或发送它在一个使用字节的媒介(例如磁盘上的文件或网络流),Encoding是一个将char数据转换为byte数据然后在另一端再次转换的绝佳方法。 (如果要支持所有可能的字符串,则需要使用基于Unicode的Encoding之一,例如Encoding.UnicodeEncoding.UTF8。 )

那么,如果你从一堆byte开始,这意味着什么呢?好吧,根据所讨论的编码,您正在使用的byte实际上可能不是Encoding输出的序列。您需要将Encoding.GetBytes视为编码操作,并将Encoding.GetChars / Encoding.GetString视为解码操作,因此您需要重新开始使用任意数组的字节并尝试解码它们。

要进行类比,请考虑图像的JPEG文件格式。这具有类似类型的编码解码,其中在这种情况下,解码数据不是string而是图像。那么,如果你采用任意字节串,那么它可以被解码为JPEG图像的几率是多少?显然,答案非常渺茫。更有可能的是,你的字节最终将沿着解码器中的一条路径走下去,“在那里,我不希望那个字节跟在另一个之后”,并且它将尽力处理假设数据它是一个有效的JPEG文件,以某种方式受损。

将任意字节数组转换为字符串时,会发生同样的事情。 UTF-8编码具有关于如何char值128及以上编码的特定规则,其中一条规则表明在匹配a之后,您将只看到与位模式10xxxxxx匹配的字节类似110xxxxx1110xxxx11110xxx的模式,它“引入”多字节序列(多个byte代表单个char)。因此,如果您的数据包含与模式10xxxxxx匹配的字节,遵循预期的“引入者”之一,则编码器只能假设数据以某种方式受损。它有什么作用?它插入了一个字符,上面写着“编码数据出现了严重错误。我尽力了。这就是出错的地方。”设计Unicode的人预测了这个确​​切的场景,并创建了一个具有这个精确含义的角色:Replacement Character

因此,如果您尝试在byte字符串中对char进行往返并且遇到此情况,那么违规byte的实际值就会丢失,而是插入替换字符。当您尝试将string转回byte数组时,它最终会编码替换字符,而不是原始数据。原始数据丢失。

您正在寻找的是编码&在另一个方向上工作的解码关系。 Encoding用于获取char数据,并找到将其临时存储为byte数据的方法。如果您想要获取byte数据并找到将其临时存储为char数据的方法,则需要为此特定目的设计编码。幸运的是,这些存在。维基百科有fairly comprehensive list个选项。 : - )

在.NET Framework中,最简单且最易于访问的选项是MIME Base-64编码,通过Convert.ToBase64StringConvert.FromBase64String公开。