C # int al byte []

172

Necesito convertir una inten una byte[]forma de hacerlo es usarla BitConverter.GetBytes(). Pero no estoy seguro si eso coincide con la siguiente especificación:

Un entero con signo XDR es un dato de 32 bits que codifica un entero en el rango [-2147483648,2147483647]. El entero se representa en notación de complemento a dos. Los bytes más y menos significativos son 0 y 3, respectivamente. Los enteros se declaran de la siguiente manera:

Fuente: RFC1014 3.2

¿Cómo podría hacer una transformación int a byte que satisfaga la especificación anterior?

Peter
fuente
Esa es una buena pregunta.
ChaosPandion el

Respuestas:

217

El RFC solo está tratando de decir que un entero con signo es un entero normal de 4 bytes con bytes ordenados de forma big endian.

Ahora, lo más probable es que esté trabajando en una máquina little-endian y BitConverter.GetBytes()le dará la byte[]inversión. Entonces podrías probar:

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

Sin embargo, para que el código sea más portátil, puede hacerlo así:

int intValue;
byte[] intBytes = BitConverter.GetBytes(intValue);
if (BitConverter.IsLittleEndian)
    Array.Reverse(intBytes);
byte[] result = intBytes;
paraciclo
fuente
77
O use ToArray. byte [] resultado = BitConverter.GetBytes (intValue) .Reverse (). ToArray ();
Lars Truijens
¿Por qué la última línea byte[] result = intBytes;? ¿No es intBytesya la matriz que quieres?
derHugo
2
@derHugo Eso es correcto, resultes redundante en este código. Sin embargo, sentí que era más pedagógico mostrar explícitamente al lector cuál era el resultado que suponer que pueden darse cuenta de que el resultado está contenido en alguna variable nombrada intBytes. Además, hacer la asignación es barato ya que no copia la memoria ni asigna nueva memoria, simplemente agrega un nuevo nombre a la matriz ya asignada. Entonces por qué no hacerlo?
Paracycle
41

Aquí hay otra forma de hacerlo: como todos sabemos 1x byte = 8x bits y también, un entero "regular" (int32) contiene 32 bits (4 bytes). Podemos usar el operador >> para desplazar los bits a la derecha (el operador >> no cambia el valor).

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]);
Maciek
fuente
1
El inicializador de matriz y el xor (^) y los & 0xFFbits son innecesarios.
dtb
66
Es big-endian, por lo que MSB se almacena primero, por lo que debe invertir sus índices.
Marcin Deptuła
77
¿Podemos agregar un ejemplo de ir en sentido contrario? (bytes de vuelta al entero)
jocull
1
Lo uso debido al rendimiento. Si no me importa esta pequeña diferencia, buscaré la respuesta aceptada.
Eterno
2
Deberías envolver ese código en un uncheckedbloque. Actualmente solo funciona si la comprobación de desbordamiento de enteros está deshabilitada en la configuración del compilador.
CodesInChaos
25

BitConverter.GetBytes(int) casi hace lo que quieres, excepto que el endianness está mal.

Puede usar el método IPAddress.HostToNetwork para intercambiar los bytes dentro del valor entero antes de usar BitConverter.GetByteso usar la clase EndianBitConverter de Jon Skeet . Ambos métodos hacen lo correcto (tm) con respecto a la portabilidad.

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

Cuando miro esta descripción, tengo la sensación de que este entero xdr es solo un entero "estándar" big endian, pero se expresa de la manera más ofuscada. Notación de complemento a dos se conoce mejor como U2, y es lo que estamos usando en los procesadores de hoy. El orden de los bytes indica que es una notación big-endian .
Entonces, respondiendo a su pregunta, debe invertir elementos en su matriz (0 <--> 3, 1 <--> 2), ya que están codificados en little-endian. Solo para asegurarse, primero debe verificar BitConverter.IsLittleEndianpara ver qué máquina está ejecutando.

Marcin Deptuła
fuente
3

¿Por qué todo este código en los ejemplos anteriores ...

Una estructura con diseño explícito actúa en ambos sentidos y no tiene impacto en el rendimiento.

Actualización: dado que hay una pregunta sobre cómo lidiar con el endianness, agregué una interfaz que ilustra cómo abstraer eso. Otra estructura de implementación puede tratar el caso contrario

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; 
}
Sten Petrov
fuente
2
¿Cómo usarías esto? y en segundo lugar, obtendría el mismo resultado incluso si ejecuta esto en 'big-endian' y 'little-endian'.
Peter
@Peter aquí hay una actualización. Todavía debe funcionar mejor que los turnos
Sten Petrov
Sip. Básicamente, una unión de C ++ implementada como estructuras de C #. Esto es súper rápido y es bueno para empacar y desempacar todo tipo de cosas que no encajan bien en las soluciones de problemas de convertidor de bits / endian. IIRC, NAudio utiliza este enfoque con muy buenos resultados.
Craig.Feied
Esta es la mejor respuesta y es un poco triste que esta no sea la mejor, pero solo te dice algo sobre el estado de la programación en 2019
John Evans,
1

La otra forma es usar BinaryPrimitives así

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

Denis
fuente
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("]");
        }
    }
}```
Александр Гром
fuente
-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);
}
Valery Rode
fuente
agregue más explicaciones
Gregor Doroschenko
Gracias por la respuesta, ¿por qué elegiste escribir una nueva implementación en lugar de usar stackoverflow.com/a/1318948/58553 ? (¿existen desventajas pro o con su solución)
Peter
En caso de que el proyecto deba desarrollarse con no solo un idioma, puede ser útil realizar similitudes de código entre idiomas en situaciones en las que se deben implementar ideas idénticas. De esta manera, puede ayudar con la detección de errores. Seguro que usa la función 'c #' 'BitConverter.GetBytes' con 'Endian' comprobando que todo lo que se necesita en la mayoría de los casos. Y más, en alguna situación puede ocurrir usar la conversión no solo a Int32 (4 bytes) o Int64 (8 bytes) sino a otro número de bytes. Gracias.
Valery Rode
Esto no es realmente universalmente utilizable en absoluto ... siempre devuelve una matriz de 8 bytes, lo que trae mucho trabajo adicional con valores de entrada que no son entradas de 64 bits. Lógicamente, quiero una matriz de 2 bytes al convertir un Int16.
Nyerguds