C#int转换为byte []


172

我需要将转换intbyte[]一种方法BitConverter.GetBytes()。但不确定是否符合以下规范:

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

资源: RFC1014 3.2

我如何做一个可以满足上述规范的整数到字节的转换?


这是一个好问题。
2009年

Answers:


217

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;

7
或使用ToArray。byte []结果= BitConverter.GetBytes(intValue).Reverse()。ToArray();
Lars Truijens 2011年

为什么最后一行byte[] result = intBytes;?还不intBytes是您想要的数组吗?
derHugo

2
@derHugo是的,result在此代码段中是多余的。但是,我认为,向读者明确表明结果是比在假定他们可以弄清楚结果包含在名为的某个变量中更具教学意义intBytes。此外,进行分配很便宜,因为它既不复制内存也不分配新的内存,它只是向已分配的数组添加一个新名称。那么为什么不这样做呢?
摩托车

41

这是另一种方式:众所周知1x字节= 8x位,并且,“常规”整数(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不需要数组初始化程序和xor(^)和位。
dtb

6
它是big-endian,因此首先存储MSB,因此您应该反转索引。
MarcinDeptuła09年

7
我们可以添加一个相反的例子吗?(字节返回整数)
jocull,

1
由于性能,我使用它。如果我不关心这个差异,我将寻求公认的答案。
永恒的

2
您应该将该代码包装在一个unchecked块中。当前,只有在编译器设置中禁用了整数溢出检查时,它才有效。
CodesInChaos

25

BitConverter.GetBytes(int) 几乎可以满足您的要求,除了字节序错误。

在使用或使用Jon Skeet的EndianBitConverter类之前,可以使用IPAddress.HostToNetwork方法在整数值内交换字节。两种方法都针对可移植性做了正确的事情。BitConverter.GetBytes

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

3

当我看一下这个描述时,我会感觉到,这个xdr整数只是一个大端的“标准”整数,但是它以最模糊的方式表示。最好将U2称为2的补码,这就是我们在当今的处理器上使用的。字节顺序表示这是一个大端符号。
因此,在回答您的问题时,您应该对数组中的元素求逆(0 <-> 3、1 <-> 2),因为它们是以小尾数编码的。只是为了确保,您应该首先检查BitConverter.IsLittleEndian一下正在运行的计算机。


3

为什么上面的示例中所有这些代码...

具有显式布局的结构会同时起作用,并且不会影响性能。

更新:由于存在有关如何处理字节序的问题,因此我添加了一个界面,该界面说明了如何对其进行抽象。另一个实现结构可以处理相反的情况

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
您将如何使用它?其次,即使在'big-endian'和'little-endian'上运行,您也会得到相同的结果。
彼得

@Peter这是一个更新。它仍然比轮班表现更好
Sten Petrov

对。基本上,将c ++联合实现为c#结构。这是超快的,非常适合打包和解压缩所有不太适合bitconverter / endian问题解决方案的东西。IIRC,NAudio使用此方法效果非常好。
Craig.Feied

这是最好的答案,很遗憾,这不是最好的答案,但只是告诉您一些有关2019
John Evans


1

另一种方法是像这样使用BinaryPrimitives

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


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("]");
        }
    }
}```

-1
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 '18

感谢您的回答,您为什么选择编写一个新的实现而不是使用stackoverflow.com/a/1318948/58553?(您的解决方案有什么优点或缺点)
彼得

如果项目不仅需要使用一种语言来开发,在必须实现相同思想的情况下,实现语言之间的代码相似性可能很有用。这样可以帮助发现错误。确保将'c#'函数'BitConverter.GetBytes'与'Endian'一起使用,以检查大多数情况下所需的所有内容。而且,在某些情况下可能不仅使用转换为Int32(4字节)或Int64(8字节),而且还转换为其他字节数。谢谢。
Valery Rode '18

实际上,这根本不是通用的……它总是返回一个8字节的数组,这对于不是64位int的输入值会带来很多额外的工作。从逻辑上讲,当转换时,我需要2个字节的数组Int16
Nyerguds
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.