【问题标题】:Why do I get a different value after turning an integer into ASCII and then back to an integer?为什么将整数转换为 ASCII 然后再转换为整数后会得到不同的值?
【发布时间】:2020-10-08 13:40:30
【问题描述】:

为什么,当我将 INT 值转换为字节和 ASCII 并返回时,我得到另一个值?

例子:

var asciiStr = new string(Encoding.ASCII.GetChars(BitConverter.GetBytes(2000)));
var intVal = BitConverter.ToInt32(Encoding.ASCII.GetBytes(asciiStr), 0);
Console.WriteLine(intVal);

// Result: 1855

【问题讨论】:

  • 因为 ASCII 用于字符串编码(即人类可读的文本),而不是任意二进制数据的编码。将任意字节数组解释为字符串,然后尝试以原始形式返回相同的字节数组通常是不可能的。

标签: c# ascii


【解决方案1】:

ASCII 只有 7 位 - 不支持 127 以上的代码点。不支持的字符将根据the docs on Encoding.ASCII 转换为?

此属性返回的 ASCIIEncoding 对象可能没有适合您的应用的行为。它使用替换回退来用问号(“?”)字符替换无法编码的每个字符串和无法解码的每个字节。

所以2000十进制=D0 07 00 00十六进制(小端)=[unsupported character] [BEL character] [NUL character] [NUL character]=? [BEL character] [NUL character] [NUL character]=3F 07 00 00十六进制(小端)=1855十进制。

【讨论】:

    【解决方案2】:

    TL;DR:一切都很好。但你是character replacement的受害者。

    我们从2000 开始。首先让我们承认,这个数字可以用十六进制表示为0x000007d0

    BitConverter.GetBytes

    BitConverter.GetBytes(2000) 是一个 4 字节的数组,因为 2000 是一个 32 位整数文字。因此,32 位整数表示,采用小端(最低有效字节在前),由以下字节序列{ 0xd0, 0x07, 0x00, 0x00 } 给出。在十进制中,这些相同的字节是{ 208, 7, 0, 0 }

    编码.ASCII.GetChars

    哦哦!问题。这就是你可能出人意料的地方。

    您要求系统将这些字节解释为 ASCII 编码数据。问题是 ASCII 使用 0-127 的代码。值为 208 (0xd0) 的字节不对应任何可通过 ASCII 编码的字符。那么实际发生了什么?

    在解码 ASCII 时,如果遇到超出 0-127 范围的字节,则它会decodes that byte to a replacement character 并移动到下一个字节。这个替换字符是一个问号?。因此,您从 Encoding.ASCII.GetChars 返回的 4 个字符是 ?BEL (bell)、NUL (null) 和 NUL (空)。

    BEL 是代码为 7 的字符的 ASCII 名称,传统上当出现在有能力的终端上时会发出哔声。 NUL(代码 0)是一个空字符,传统上用于表示字符串的结尾。

    新字符串

    现在您从该字符数组创建一个字符串。在 C# 中,字符串完全能够在字符串主体中表示 NUL 字符,因此您的字符串中将包含两个 NUL 字符。它们可以用 "\0" 在 C# 字符串文字中表示,以防您想自己尝试。表示您拥有的字符串的 C# 字符串文字将是 "?\a\0\0" 您是否知道 BEL 字符可以用 escape sequence \a 表示?很多人没有。

    编码.ASCII.GetBytes

    现在您开始反向旅程。您的字符串完全由 ASCII 范围内的字符组成。问号的编码是代码 63 (0x3F)。 BEL 为 7,NUL 为 0。所以字节为{ 0x3f, 0x07, 0x00, 0x00 }。惊讶吗?好吧,您现在正在编码一个问号,而之前您提供的 208 (0xd0) 字节无法用 ASCII 编码表示。

    BitConverter.ToInt32

    将这四个字节转换回 32 位整数得到整数 0x0000073f,在十进制中为 1855

    【讨论】:

      【解决方案3】:

      字符串编码(ASCII、UTF8、SHIFT_JIS 等)旨在将人类语言归类为二进制(字节)形式。它不是为存储任意二进制数据而设计的,例如整数的二进制形式。

      虽然您的二进制数据将被解释为字符串,但其中一些信息会丢失,这意味着以这种方式存储二进制数据在一般情况下会失败。您可以使用以下代码查看失败的地方:

      for (int i = 0; i < 255; ++i)
      {
          var byteData = new byte[] { (byte)i };
          var stringData = System.Text.Encoding.ASCII.GetString(byteData);
          var encodedAsBytes = System.Text.Encoding.ASCII.GetBytes(stringData);
      
          Console.WriteLine("{0} vs {1}", i, (int)encodedAsBytes[0]);
      }
      

      Try it online

      正如你所看到的,它一开始就很好,因为所有的字符代码都对应于 ASCII 字符,但是一旦我们得到数字(即 128 及以上),我们开始需要超过 7 位来存储二进制价值。此时它不再被正确解码,我们开始看到 63 而不是输入值。

      最终,您将遇到使用任何字符串编码对二进制数据进行编码的问题。您需要选择一种专门用于将二进制数据存储为字符串的编码方法。

      两种流行的方法是:

      十六进制示例(使用十六进制方法here):

      int initialValue = 2000;
      Console.WriteLine(initialValue);
      
      // Convert from int to bytes and then to hex
      byte[] bytesValue = BitConverter.GetBytes(initialValue);
      string stringValue = ByteArrayToString(bytesValue);
      
      Console.WriteLine("As hex: {0}", stringValue); // outputs D0070000
      
      // Convert form hex to bytes and then to int
      byte[] decodedBytesValue = StringToByteArray(stringValue);
      int intValue = BitConverter.ToInt32(decodedBytesValue, 0);
      Console.WriteLine(intValue);
      

      Try it online

      Base64 示例:

      int initialValue = 2000;
      Console.WriteLine(initialValue);
      
      // Convert from int to bytes and then to base64
      byte[] bytesValue = BitConverter.GetBytes(initialValue);
      string stringValue = Convert.ToBase64String(bytesValue);
      
      Console.WriteLine("As base64: {0}", stringValue); // outputs 0AcAAA==
      
      // Convert form base64 to bytes and then to int
      byte[] decodedBytesValue = Convert.FromBase64String(stringValue);
      int intValue = BitConverter.ToInt32(decodedBytesValue, 0);
      Console.WriteLine(intValue);
      

      Try it online

      附:如果您只是想将整数转换为字符串(例如“2000”),那么您可以简单地使用.ToString():

      int initialValue = 2000;
      string stringValue = initialValue.ToString();
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2014-06-18
        • 2019-10-16
        • 1970-01-01
        • 1970-01-01
        • 2014-09-03
        • 1970-01-01
        • 2014-03-31
        相关资源
        最近更新 更多