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. байт [] результат = BitConverter.GetBytes (intValue) .Reverse (). ToArray ();
Lars Truijens

чому останній рядок 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
Ініціалізатор масиву та xor (^) та & 0xFFбіти не потрібні.
dtb

6
Це великий ендіан, тому MSB зберігається першим, тому вам слід обернути свої індекси.
Marcin Deptuła

7
Чи можемо ми додати приклад того, як йти навпаки? (байти назад до цілого числа)
Jocull

1
Я використовую це через продуктивність. Якщо мене не хвилює ця невелика різниця, я піду для прийнятої відповіді.
позачасовий

2
Ви повинні загортати цей код у uncheckedблок. В даний час він працює лише в тому випадку, якщо в налаштуваннях компілятора вимкнено перевірку цілорічного переповнення.
CodesInChaos

25

BitConverter.GetBytes(int) майже робить те, що ви хочете, за винятком випадковості не так.

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

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

3

Коли я дивлюся на цей опис, у мене виникає відчуття, що це ціле число xdr - це просто велике ціле "стандартне" ціле число, але воно виражається найбільш заплутаним чином. Позначення доповнення двох краще знати як 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".
Петро

@Peter ось оновлення. Це все ж повинно виконуватись краще, ніж у змінах
Стен Петров

Так. В основному союз c ++, реалізований як c # структури. Це дуже швидко і добре підходить для упаковки та розпакування всіх речей, які не вписуються непогано в рішення проблем бітконвертера / ендіан. IIRC, NAudio використовує такий підхід для дуже хорошого ефекту.
Craig.Feied

Це найкраща відповідь, і дивно сумно, що це не верх, а просто розповідає про стан програмування у 2019 році
Джон Еванс


1

Інший спосіб - використовувати подібні BinaryPrimitive

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.