C # int para byte []

172

Eu preciso converter um intpara uma byte[]maneira de fazê-lo é usar BitConverter.GetBytes(). Mas não tenho certeza se isso corresponde à seguinte especificação:

Um número inteiro assinado por XDR é um dado de 32 bits que codifica um número inteiro no intervalo [-2147483648,2147483647]. O número inteiro é representado na notação de complemento de dois. Os bytes mais e menos significativos são 0 e 3, respectivamente. Os números inteiros são declarados da seguinte maneira:

Fonte: RFC1014 3.2

Como eu poderia fazer uma transformação de byte int que satisfizesse a especificação acima?

Peter
fonte
Essa é uma boa pergunta.
ChaosPandion

Respostas:

217

O RFC está apenas tentando dizer que um número inteiro assinado é um número inteiro normal de 4 bytes com bytes ordenados de maneira big-endian.

Agora, você provavelmente está trabalhando em uma máquina little-endian e BitConverter.GetBytes()lhe dará o byte[]contrário. Então você pode tentar:

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

Para que o código seja mais portátil, você pode fazer o seguinte:

int intValue;
byte[] intBytes = BitConverter.GetBytes(intValue);
if (BitConverter.IsLittleEndian)
    Array.Reverse(intBytes);
byte[] result = intBytes;
paracycle
fonte
7
Ou use o ToArray. byte [] resultado = BitConverter.GetBytes (intValue) .Reverse (). ToArray ();
Lars Truijens
por que a última linha byte[] result = intBytes;? intBytesjá não é o array que você deseja?
DerHugo 15/04/19
2
@derHugo Isso está correto, resulté redundante neste código. No entanto, senti que era mais pedagógico mostrar explicitamente ao leitor qual era o resultado do que supor que eles possam descobrir que o resultado está contido em alguma variável denominada intBytes. Além disso, fazer a atribuição é barato, pois não copia a memória nem aloca nova memória, apenas adiciona um novo nome à matriz já alocada. Então, por que não fazer isso?
paracycle
41

Aqui está outra maneira de fazer isso: como todos sabemos 1x byte = 8x bits e também um número inteiro "regular" (int32) contém 32 bits (4 bytes). Podemos usar o operador >> para mudar os bits para a direita (o operador >> não altera o 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
fonte
1
O inicializador de matriz e o xor (^) e & 0xFFbits são desnecessários.
dtb 23/08/09
6
É big-endian, então o MSB é armazenado primeiro, então você deve inverter seus índices.
Marcin Deptuła 23/08/09
7
Podemos adicionar um exemplo de seguir o caminho oposto? (bytes de volta ao número inteiro)
jocull 23/05
1
Eu uso isso por causa do desempenho. Se eu não me importo com essa pequena diferença, vou buscar a resposta aceita.
Timeless
2
Você deve agrupar esse código em um uncheckedbloco. Atualmente, ele só funciona se a verificação de estouro inteiro estiver desabilitada nas configurações do compilador.
CodesInChaos 03/03
25

BitConverter.GetBytes(int) quase faz o que você quer, exceto que o endianness está errado.

Você pode usar o método IPAddress.HostToNetwork para trocar os bytes no valor inteiro antes de usar BitConverter.GetBytesou usar a classe EndianBitConverter de Jon Skeet . Ambos os métodos fazem a coisa certa (tm) em relação à portabilidade.

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

Quando olho para essa descrição, sinto que esse número inteiro xdr é apenas um número inteiro "padrão" big-endian, mas é expresso da maneira mais ofuscada. A notação de complemento do Two é mais conhecida como U2, e é o que estamos usando nos processadores de hoje. A ordem dos bytes indica que é uma notação big endian .
Portanto, respondendo à sua pergunta, você deve inverter os elementos em sua matriz (0 <--> 3, 1 <--> 2), pois eles são codificados em little-endian. Apenas para ter certeza, você deve primeiro verificar BitConverter.IsLittleEndianpara ver em qual máquina está executando.

Marcin Deptuła
fonte
3

Por que todo esse código nos exemplos acima ...

Uma estrutura com layout explícito atua nos dois sentidos e não tem impacto no desempenho.

Atualização: Como há uma pergunta sobre como lidar com endianness, adicionei uma interface que ilustra como abstrair isso. Outra estrutura de implementação pode lidar com o caso oposto

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
fonte
2
Como você usaria isso? e, em segundo lugar, você obteria o mesmo resultado mesmo executando isso em 'big-endian' e em 'little-endian'.
Peter
@ Peter, aqui está uma atualização. Ele ainda deve executar melhor do que mudanças
Sten Petrov
Sim. Basicamente, uma união c ++ implementada como estruturas c #. Isso é super rápido e é bom para empacotar e descompactar todos os tipos de coisas que não se encaixam perfeitamente nas soluções de problemas do conversor de bits / endian. IIRC, o NAudio usa essa abordagem com muito bom efeito.
Craig.Feied
Esta é a melhor resposta e é meio triste que este não é o topo, mas apenas lhe diz algo sobre o estado da programação em 2019
John Evans
1

A outra maneira é usar BinaryPrimitives assim

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

Denis
fonte
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("]");
        }
    }
}```
Александр Гром
fonte
-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
fonte
por favor, adicione mais explicações
Gregor Doroschenko
Obrigado pela resposta, por que você escolheu escrever uma nova implementação em vez de usar o stackoverflow.com/a/1318948/58553 ? (há algum prós ou contras com a sua solução) #
Peter Peter
Caso o projeto precise ser desenvolvido com não apenas um idioma, pode ser útil perceber a similaridade do código entre os idiomas na situação em que idéias idênticas devem ser implementadas. Desta forma, pode ajudar com os erros de captura. Claro, usando a função 'c #' BitConverter.GetBytes 'com' Endian 'verificando tudo o que é necessário na maioria dos casos. Além disso, em algumas situações, use a conversão não apenas para Int32 (4 bytes) ou Int64 (8 bytes), mas para outro número de bytes. Obrigado.
Valery Rode
Isso não é realmente universalmente utilizável ... sempre retorna uma matriz de 8 bytes, o que traz muito trabalho extra com valores de entrada que não são ints de 64 bits. Eu quero logicamente uma matriz de 2 bytes ao converter um arquivo Int16.
Nyerguds