C # int to byte []


172

Мне нужно преобразовать intв byte[]один из способов сделать это заключается в использовании BitConverter.GetBytes(). Но я не уверен, что это соответствует следующей спецификации:

Целое число со знаком XDR - это 32-разрядное значение, которое кодирует целое число в диапазоне [-2147483648,2147483647]. Целое число представлено в двух дополнительных обозначениях. Самые старшие и младшие байты равны 0 и 3 соответственно. Целые числа объявляются следующим образом:

Источник: RFC1014 3.2

Как я могу сделать преобразование int в байты, которое удовлетворяло бы вышеупомянутой спецификации?


Это хороший вопрос.
ChaosPandion

Ответы:


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 [] result = BitConverter.GetBytes (intValue) .Reverse (). ToArray ();
Ларс Труйенс

почему последняя строка byte[] result = intBytes;? intBytesуже не тот массив, который вы хотите?
derHugo

2
@derHugo Это правильно, resultизбыточно в этом фрагменте кода. Тем не менее, я чувствовал, что было более педагогично явно показать читателям, каков был результат, чем предполагать, что они могут понять, что результат содержится в некоторой переменной с именем intBytes. Кроме того, выполнение присваивания является дешевым, поскольку оно не копирует и не выделяет новую память, а просто добавляет новое имя к уже выделенному массиву. Так почему бы не сделать это?
paracycle

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
Инициализатор массива и xor (^) и & 0xFFбиты не нужны.
DTB

6
Это big-endian, поэтому MSB хранится в первую очередь, поэтому вы должны инвертировать свои индексы.
Марчин Дептула

7
Можем ли мы добавить пример идти противоположным путем? (байты обратно в целое число)
jocull

1
Я использую это из-за производительности. Если меня не волнует эта небольшая разница, я пойду на принятый ответ.
Вечный

2
Вы должны обернуть этот код в uncheckedблок. В настоящее время это работает, только если целочисленная проверка переполнения отключена в настройках компилятора.
CodesInChaos

25

BitConverter.GetBytes(int) почти делает то, что вы хотите, за исключением того, что порядок байтов не так.

Вы можете использовать метод IPAddress.HostToNetwork, чтобы поменять байты внутри целочисленного значения перед использованием BitConverter.GetBytesили использованием класса EndianBitConverter Джона Скита . Оба метода делают правильные вещи (тм) в отношении переносимости.

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

3

Когда я смотрю на это описание, у меня возникает ощущение, что это целое число xdr является просто «стандартным» целым числом с прямым порядком байтов, но оно выражено самым запутанным образом. Обозначение дополнения Two лучше известно как U2, и это то, что мы используем на современных процессорах. Порядок байтов указывает, что это запись с прямым порядком байтов .
Итак, отвечая на ваш вопрос, вы должны инвертировать элементы в вашем массиве (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'.
Питер

@ Питер, вот обновление. Он все равно должен работать лучше, чем смены
Стен Петров

Ага. По сути, объединение c ++ реализовано как структура c #. Это очень быстро и хорошо для упаковки и распаковки всех видов вещей, которые не вписываются в решения проблем bitconverter / endian. IIRC, NAudio использует этот подход для очень хорошего эффекта.
Craig.Feied

Это лучший ответ, и довольно грустно, что это не вершина, а просто говорит вам кое-что о состоянии программирования в 2019 году
Джон Эванс


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);
}

пожалуйста, добавьте больше объяснений
Грегор Дорощенко

Спасибо за ответ, почему вы решили написать новую реализацию вместо использования stackoverflow.com/a/1318948/58553 ? (есть ли плюсы или минусы в вашем решении)
Питер

В случае, когда необходимо разработать проект не только с одним языком, может быть полезно реализовать сходство кода между языками в ситуации, когда должны быть реализованы идентичные идеи. Этот способ может помочь с обнаружением ошибок. Обязательно используйте 'c #' функцию 'BitConverter.GetBytes' с 'Endian', проверяя, что все, что нужно в большинстве случаев. Более того, в некоторых ситуациях может использоваться преобразование не только в Int32 (4 байта) или Int64 (8 байтов), но и в другое количество байтов. Спасибо.
Валерий Роде

Это не совсем универсально для использования вообще ... он всегда возвращает 8-байтовый массив, который приносит много дополнительной работы с входными значениями, которые не являются 64-битными. Логически я хотел бы 2-байтовый массив при преобразовании Int16.
Nyerguds
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.