HOWTO · Csharp

在 C# 中将 Int 转换为字节

在这篇文章中,我们将介绍几种在 C# 中将 Int 转换为 Byte 的不同技术。

本页内容

在 C# 中,合适的转换方式取决于你要存储什么。byte 可存放一个介于 0 到 255 之间的无符号值。byte[] 则存放用于表示较大值的字节。若结果应为单个字节,请使用 Convert.ToByte;若需要 int 的二进制表示,请使用 BitConverter.GetBytes 或流。对于文件或网络格式,还应明确指定所需的字节序。

在 byte 和 byte[] 之间选择

int 是有符号的 32 位数,而 byte 是一个无符号的 8 位数。因此,只有当 int 的值介于 0 到 255 之间时,才能将其转换为 byte。相比之下,byte[] 可以保留 int 的全部四个字节;字节排列顺序决定了其他系统如何还原该数值。

对于确定能放入单个字节的值(例如较小的计数或通道值),请选择标量转换。对于序列化、二进制协议或要求字节的 API,请选择数组。将数字转换为十进制文本再编码是另一种操作:它存储的是 1、2、3 这样的字符,而不是整数 123 的二进制表示。

使用 byte 和 Convert.ToByte 转换值

Convert.ToByte 返回单个标量 byte。字符串重载会解析十进制数字文本,数值重载则转换受支持的数值类型。对于整数变量,转换前应确认其值介于 0 到 255 之间。未经检查的强制转换可能会丢弃高位,而不会报告溢出。

运行此示例:

本节中的代码以及显示的标签或输出仍为英文。

using System;

class Program
{
    static void Main()
    {
        string text = "42";
        byte fromText = Convert.ToByte(text);
        byte fromNumber = Convert.ToByte(90);
        Console.WriteLine($"{fromText}, {fromNumber}");
    }
}
42, 90

字符串输入为 null 时会转换为 0。不是数字的文本会导致 FormatException,而解析后超出 0 到 255 范围的值会导致 OverflowException。

下一个示例会处理这两种失败情况,并打印捕获到的异常类型,以便区分它们。FormatException 来自非数字文本;OverflowException 则来自解析后大于 255 的值。示例输出中的异常名称保持英文。

运行此失败处理示例:

using System;

class Program
{
    static void Main()
    {
        string[] inputs = { "twelve", "300" };

        foreach (string input in inputs)
        {
            try
            {
                byte value = Convert.ToByte(input);
                Console.WriteLine($"{input} -> {value}");
            }
            catch (FormatException)
            {
                Console.WriteLine($"{input} -> FormatException");
            }
            catch (OverflowException)
            {
                Console.WriteLine($"{input} -> OverflowException");
            }
        }
    }
}

字符串与基数重载 Convert.ToByte(value, fromBase) 同样返回单个字节。它支持基数 2、8、10 和 16;并不支持从 2 到 36 的所有基数。例如,Convert.ToByte("FF", 16) 会解析十六进制值。包含无效数字字符或超出 byte 范围的值会导致转换错误;基数不在支持范围内则会导致 ArgumentException。不要将此重载描述为把整数转换成字节数组。

使用 BitConverter.GetBytes 将 int 转换为 byte[]

BitConverter.GetBytes(int) 会返回包含该整数二进制表示的四个字节。字节顺序取决于当前机器,可通过 BitConverter.IsLittleEndian 查看。下面的完整示例会以十进制数字打印这些字节,并指出机器使用的本机字节序。示例中的 Little-endian 和 Big-endian 标签保持英文。

本节中的代码以及显示的标签或输出仍为英文。

using System;

class Program
{
    static void Main()
    {
        int value = 123456789;
        byte[] bytes = BitConverter.GetBytes(value);
        Console.WriteLine(string.Join(" ", bytes));
        Console.WriteLine(BitConverter.IsLittleEndian ? "Little-endian" : "Big-endian");
    }
}

当接收端使用相同的字节序约定时,这种方法很方便。如果要将字节存储或发送到其他系统,应先约定格式,不要假设两台机器使用相同的字节顺序。

使用 BinaryWriter 和 MemoryStream 将 int 写入 byte[]

如果代码已经在向流写入一系列值,可以用 BinaryWriter 写入 int,再通过 MemoryStream.ToArray() 获取累计的字节。BinaryWriter 会按小端顺序写入其原始数值类型。读取流内容前应先释放 writer,确保缓冲工作已完成;释放 writer 也会释放流,但之后仍可调用 ToArray()。

本节中的代码以及显示的标签或输出仍为英文。

using System;
using System.IO;

class Program
{
    static void Main()
    {
        int value = 987654321;
        byte[] bytes;

        using (MemoryStream stream = new MemoryStream())
        {
            using (BinaryWriter writer = new BinaryWriter(stream))
            {
                writer.Write(value);
            }

            bytes = stream.ToArray();
        }

        Console.WriteLine(string.Join(" ", bytes));
    }
}

当需要按顺序序列化多个字段时,这种方式很有用。生成的数组包含流的全部内容,而不只是最后写入的整数。如果协议指定了其他字节序,请使用该协议要求的明确顺序。

将 int 转换为 byte[] 时控制字节顺序

端序决定最低有效字节或最高有效字节哪个先出现。BitConverter.GetBytes 使用机器的本机字节序,因此只有在本机字节序与所需格式不同时,才应反转数组副本。下面的示例会为同一个值分别生成小端和大端表示,不受机器本机字节序影响;输出中的 Little-endian 和 Big-endian 标签保持英文。

本节中的代码以及显示的标签或输出仍为英文。

using System;

class Program
{
    static void Main()
    {
        int value = 567890123;
        byte[] native = BitConverter.GetBytes(value);
        byte[] littleEndian = (byte[])native.Clone();
        byte[] bigEndian = (byte[])native.Clone();

        if (!BitConverter.IsLittleEndian)
        {
            Array.Reverse(littleEndian);
        }

        if (BitConverter.IsLittleEndian)
        {
            Array.Reverse(bigEndian);
        }

        Console.WriteLine("Little-endian: " + string.Join(" ", littleEndian));
        Console.WriteLine("Big-endian: " + string.Join(" ", bigEndian));
    }
}

应在写入或读取字节的边界处指定所需顺序。对于定宽整数表示,反转本地数组是恰当的做法,但这并不能定义完整的序列化格式:格式还必须指定宽度、有无符号,以及多个字段的排列顺序。