C#整数转换为字节数组

215
我需要将一个int转换为byte[],其中一种方法是使用BitConverter.GetBytes()。但我不确定是否符合以下规范:

XDR有符号整数是32位数据,它在[-2147483648, 2147483647]范围内编码一个整数。该整数以二进制补码表示。最高位字节为0,最低位字节为3。声明整数如下:

来源:RFC1014 3.2

我应该如何进行int到byte的转换以满足上述规范?

9个回答

266

RFC只是在说有符号整数是一个正常的4字节整数,其字节以大端方式排序。

现在,你很可能正在使用小端机器,BitConverter.GetBytes()会给你相反顺序的byte[]。所以你可以尝试:

int intValue;
byte[] intBytes = BitConverter.GetBytes(intValue);
Array.Reverse(intBytes);
byte[] result = intBytes;

为使代码最具可移植性,可以按如下方式进行:

int intValue;
byte[] intBytes = BitConverter.GetBytes(intValue);
if (BitConverter.IsLittleEndian)
    Array.Reverse(intBytes);
byte[] result = intBytes;

9
或者使用ToArray。byte[] result = BitConverter.GetBytes(intValue).Reverse().ToArray(); (注:该代码用于将整数转换为字节数组并翻转字节顺序) - Lars Truijens
2
为什么最后一行是 byte[] result = intBytes;?难道 intBytes 不是你想要的数组吗? - derHugo
4
@derHugo的说法正确,这段代码中的“result”是多余的。然而,我认为明确向读者展示结果比假设他们可以找到结果在名为“intBytes”的变量中更有教育意义。此外,执行赋值操作很便宜,因为它不会复制内存也不会分配新内存,它只是将一个新名称添加到已分配的数组中。那么为什么不这样做呢? - paracycle

55

这里有另一种方法:众所周知,1个字节= 8个位,而且“常规”整数(int32)包含32个位(4个字节)。我们可以使用>>运算符向右移动位(>>运算符不会更改值)。

int intValue = 566;

byte[] bytes = new byte[4];

bytes[0] = (byte)(intValue >> 24);
bytes[1] = (byte)(intValue >> 16);
bytes[2] = (byte)(intValue >> 8);
bytes[3] = (byte)intValue;

Console.WriteLine("{0} breaks down to : {1} {2} {3} {4}",
    intValue, bytes[0], bytes[1], bytes[2], bytes[3]);

1
数组初始化程序和异或(^)以及& 0xFF位是不必要的。 - dtb
6
它是大端序的,因此最高有效位先存储,所以您应该反转索引。 - Marcin Deptuła
7
我们能否添加一个相反的例子?(将字节转换回整数) - jocull
1
我使用这个是因为它的性能更好。如果我不在意这个“微小”的差异,我会选择被接受的答案。 - Timeless
3
你应该把那段代码放在一个unchecked块中。目前它只有在编译器设置中禁用整数溢出检查时才能正常工作。 - CodesInChaos

29

BitConverter.GetBytes(int) 差不多是你所需的,只是字节序(即大端或小端)不正确。

可以使用 IPAddress.HostToNetwork 方法在使用 BitConverter.GetBytes 之前交换整数值中的字节,或者使用 Jon Skeet 的 EndianBitConverter 类。这两种方法都可以正确处理可移植性。

int value;
byte[] bytes = BitConverter.GetBytes(IPAddress.HostToNetworkOrder(value));

21
另一种方法是使用类似于BinaryPrimitives的工具,操作如下:

byte[] intBytes = BitConverter.GetBytes(123); int actual = BinaryPrimitives.ReadInt32LittleEndian(intBytes);


3
这应该是最佳答案,但考虑添加:https://learn.microsoft.com/en-us/dotnet/api/system.bitconverter.islittleendian?view=net-5.0 - Vinigas
这应该是被接受的答案。但请注意 - 它从.NET 5.0和.NET Core 2.1开始提供。 - rustyx
1
什么意思是“123”? - john k
只是一个例子 - "int actual" 应该最终变成 123。 - undefined

7

为什么以上示例中有这么多代码...

使用显式布局的结构体可以双向操作且没有性能损失。

更新:由于有关如何处理字节序的问题,我添加了一个接口来说明如何抽象此过程。另一个实现结构体可以处理相反的情况。

public interface IIntToByte
{
    Int32 Int { get; set;}

    byte B0 { get; }
    byte B1 { get; }
    byte B2 { get; }
    byte B3 { get; }
}

[StructLayout(LayoutKind.Explicit)]
public struct IntToByteLE : UserQuery.IIntToByte
{
    [FieldOffset(0)]
    public Int32 IntVal;

    [FieldOffset(0)]
    public byte b0;
    [FieldOffset(1)]
    public byte b1;
    [FieldOffset(2)]
    public byte b2;
    [FieldOffset(3)]
    public byte b3;

    public Int32 Int {
        get{ return IntVal; }
        set{ IntVal = value;}
    }

    public byte B0 => b0;
    public byte B1 => b1;
    public byte B2 => b2;
    public byte B3 => b3; 
}

2
你会如何使用这个?其次,即使在“大端”和“小端”上运行此代码,你是否会得到相同的结果? - Peter
@Peter 这里有一个更新。它应该比移位操作仍然表现更好。 - Sten Petrov
没错。基本上是将C++联合实现为C#结构体。这非常快速,适用于打包和解包各种不适合使用BitConverter/Endian问题解决方案的东西。如果我没记错的话,NAudio就是采用这种方法来取得非常好的效果的。 - Craig.Feied
1
这是最好的答案,但有点遗憾它不是排名第一,这说明了2019年编程领域的现状。 - John Evans

3
当我看到这个描述时,我有一种感觉,那就是这个xdr整数只是一个大端字节序的“标准”整数,但它以最难懂的方式表达出来。二进制补码表示法更好地称为U2,并且它是我们今天处理器上使用的表示法。字节顺序指示它是大端字节序
因此,回答您的问题,您应该反转数组中的元素(0 <--> 3,1 <--> 2),因为它们是按小端字节序编码的。为了确保,您应该首先检查BitConverter.IsLittleEndian以查看您正在运行的机器。

2

0
using static System.Console;

namespace IntToBits
{
    class Program
    {
        static void Main()
        {
            while (true)
            {
                string s = Console.ReadLine();
                Clear();
                uint i;
                bool b = UInt32.TryParse(s, out i);
                if (b) IntPrinter(i);
            }
        }

        static void IntPrinter(uint i)
        {
            int[] iarr = new int [32];
            Write("[");
            for (int j = 0; j < 32; j++)
            {
                uint tmp = i & (uint)Math.Pow(2, j);

                iarr[j] = (int)(tmp >> j);
            }
            for (int j = 32; j > 0; j--)
            {
                if(j%8==0 && j != 32)Write("|");
                if(j%4==0 && j%8 !=0) Write("'");
                Write(iarr[j-1]);
            }
            WriteLine("]");
        }
    }
}```

-2
byte[] Take_Byte_Arr_From_Int(Int64 Source_Num)
{
   Int64 Int64_Num = Source_Num;
   byte Byte_Num;
   byte[] Byte_Arr = new byte[8];
   for (int i = 0; i < 8; i++)
   {
      if (Source_Num > 255)
      {
         Int64_Num = Source_Num / 256;
         Byte_Num = (byte)(Source_Num - Int64_Num * 256);
      }
      else
      {
         Byte_Num = (byte)Int64_Num;
         Int64_Num = 0;
      }
      Byte_Arr[i] = Byte_Num;
      Source_Num = Int64_Num;
   }
   return (Byte_Arr);
}

请添加更多解释 - Gregor Doroschenko
谢谢你的回答,你为什么选择编写新的实现,而不是使用https://dev59.com/EXM_5IYBdhLWcg3wlEJH#1318948?(你的解决方案中有什么优缺点) - Peter
在需要使用多种语言开发项目的情况下,实现不同语言之间的代码相似性可能会很有用,特别是在必须实现相同想法的情况下。这种方式可以帮助捕捉错误。当然,在大多数情况下,使用“c#”函数“BitConverter.GetBytes”和“Endian”检查就足够了。此外,在某些情况下,可能需要将转换应用于除Int32(4字节)或Int64(8字节)之外的其他字节数。谢谢。 - Valery Rode
这并不是真正通用的...它总是返回一个8字节的数组,这会给那些不是64位整数的输入值带来很多额外的工作。当转换Int16时,我逻辑上想要一个2字节的数组。 - Nyerguds

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接