C # int to byte []

172

Ich muss eine intin eine byte[]einzige Möglichkeit umwandeln , es zu verwenden BitConverter.GetBytes(). Aber ich bin mir nicht sicher, ob das der folgenden Spezifikation entspricht:

Eine XDR-Ganzzahl mit Vorzeichen ist ein 32-Bit-Datum, das eine Ganzzahl im Bereich [-2147483648,2147483647] codiert. Die Ganzzahl wird in Zweierkomplementnotation dargestellt. Die höchst- und niedrigstwertigen Bytes sind 0 bzw. 3. Ganzzahlen werden wie folgt deklariert:

Quelle: RFC1014 3.2

Wie könnte ich eine Int-zu-Byte-Transformation durchführen, die die obige Spezifikation erfüllt?

Peter
quelle
Das ist eine gute Frage.
ChaosPandion

Antworten:

217

Der RFC versucht nur zu sagen, dass eine vorzeichenbehaftete Ganzzahl eine normale 4-Byte-Ganzzahl ist, deren Bytes auf Big-Endian-Weise geordnet sind.

Jetzt arbeiten Sie höchstwahrscheinlich an einer Little-Endian-Maschine und BitConverter.GetBytes()geben Ihnen das byte[]Gegenteil. Sie könnten also versuchen:

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

Damit der Code am portabelsten ist, können Sie dies folgendermaßen tun:

int intValue;
byte[] intBytes = BitConverter.GetBytes(intValue);
if (BitConverter.IsLittleEndian)
    Array.Reverse(intBytes);
byte[] result = intBytes;
Paracycle
quelle
7
Oder verwenden Sie ToArray. byte [] result = BitConverter.GetBytes (intValue) .Reverse (). ToArray ();
Lars Truijens
warum die letzte Zeile byte[] result = intBytes;? ist nicht intBytesschon das Array, das Sie wollen?
derHugo
2
@derHugo Das stimmt, resultist in diesem Codestück überflüssig. Ich fand es jedoch pädagogischer, dem Leser explizit zu zeigen, was das Ergebnis war, als anzunehmen, dass er herausfinden kann, dass das Ergebnis in einer Variablen mit dem Namen enthalten ist intBytes. Außerdem ist die Zuweisung billig, da weder der Speicher kopiert noch neuer Speicher zugewiesen wird, sondern lediglich dem bereits zugewiesenen Array ein neuer Name hinzugefügt wird. Warum also nicht?
Paracycle
41

Hier ist eine andere Möglichkeit: Wie wir alle wissen, enthält 1x Byte = 8x Bits und eine "reguläre" Ganzzahl (int32) enthält 32 Bits (4 Bytes). Wir können den Operator >> verwenden, um die Bits nach rechts zu verschieben (der Operator >> ändert den Wert nicht.)

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
quelle
1
Der Array-Initialisierer und das xor (^) und die & 0xFFBits sind nicht erforderlich.
dtb
6
Es ist Big-Endian, daher wird MSB zuerst gespeichert, daher sollten Sie Ihre Indizes umkehren.
Marcin Deptuła
7
Können wir ein Beispiel für den umgekehrten Weg hinzufügen? (Bytes zurück zur ganzen Zahl)
Jocull
1
Ich benutze dies wegen der Leistung. Wenn mir dieser kleine Unterschied egal ist , werde ich mich für die akzeptierte Antwort entscheiden.
Zeitlos
2
Sie sollten diesen Code in einen uncheckedBlock einschließen. Derzeit funktioniert es nur, wenn die Überprüfung des Ganzzahlüberlaufs in den Compilereinstellungen deaktiviert ist.
CodesInChaos
25

BitConverter.GetBytes(int) macht fast was du willst, außer dass die Endianness falsch ist.

Sie können die IPAddress.HostToNetwork- Methode verwenden, um die Bytes innerhalb des Integer-Werts auszutauschen, bevor Sie BitConverter.GetBytesdie EndianBitConverter-Klasse von Jon Skeet verwenden oder verwenden . Beide Methoden machen das Richtige (tm) in Bezug auf die Portabilität.

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

Wenn ich mir diese Beschreibung ansehe, habe ich das Gefühl, dass diese xdr-Ganzzahl nur eine Big-Endian-Ganzzahl ist, aber auf die verschleierteste Weise ausgedrückt wird. Die Komplementnotation von Two ist besser als U2 bekannt und wird auf heutigen Prozessoren verwendet. Die Bytereihenfolge gibt an, dass es sich um eine Big-Endian- Notation handelt.
Wenn Sie Ihre Frage beantworten, sollten Sie die Elemente in Ihrem Array (0 <--> 3, 1 <--> 2) umkehren, da sie in Little-Endian codiert sind. Um sicherzugehen, sollten Sie zuerst überprüfen BitConverter.IsLittleEndian, auf welchem ​​Computer Sie ausgeführt werden.

Marcin Deptuła
quelle
3

Warum all dieser Code in den obigen Beispielen ...

Eine Struktur mit explizitem Layout verhält sich in beide Richtungen und hat keinen Leistungseinbruch.

Update: Da es eine Frage zum Umgang mit Endianness gibt, habe ich eine Schnittstelle hinzugefügt, die zeigt, wie man das abstrahiert. Eine andere Implementierungsstruktur kann den umgekehrten Fall behandeln

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
quelle
2
Wie würden Sie das nutzen? und zweitens würden Sie das gleiche Ergebnis erzielen, selbst wenn Sie dies auf "Big-Endian" und "Little-Endian" ausführen.
Peter
@ Peter hier ist ein Update. Es sollte immer noch besser als Schichten sein
Sten Petrov
Jep. Grundsätzlich ist eine c ++ - Union als c # -Struktur implementiert. Dies ist superschnell und eignet sich zum Ein- und Auspacken aller Arten von Dingen, die nicht gut in die Bitconverter / Endian-Problemlösungen passen. IIRC, NAudio verwendet diesen Ansatz mit sehr guter Wirkung.
Craig.Feied
Dies ist die beste Antwort und es ist ein bisschen traurig, dass dies nicht die Spitze ist, sondern nur etwas über den Stand der Programmierung im Jahr 2019 sagt
John Evans
2

Wenn Sie allgemeinere Informationen zu verschiedenen Methoden zur Darstellung von Zahlen, einschließlich Two's Complement, wünschen, schauen Sie sich Folgendes an:

Two's Complement und Signed Number Representation auf Wikipedia

Eric J.
quelle
1

Die andere Möglichkeit besteht darin, BinaryPrimitives wie folgt zu verwenden

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

Denis
quelle
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("]");
        }
    }
}```
Александр Гром
quelle
-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 ritt
quelle
Bitte fügen Sie weitere Erklärungen hinzu
Gregor Doroschenko
Vielen Dank für die Antwort. Warum haben Sie sich entschieden, eine neue Implementierung zu schreiben, anstatt stackoverflow.com/a/1318948/58553 zu verwenden ? (Gibt es irgendwelche Vor- oder Nachteile mit Ihrer Lösung)
Peter
Wenn das Projekt nicht nur mit einer Sprache entwickelt werden muss, kann es nützlich sein, Codeähnlichkeit zwischen Sprachen zu erkennen, wenn identische Ideen implementiert werden müssen. Dieser Weg kann bei Fehlern helfen. Sicher mit der 'c #' Funktion 'BitConverter.GetBytes', wobei 'Endian' in den meisten Fällen alles überprüft, was benötigt wird. In einigen Situationen kann die Konvertierung nicht nur in Int32 (4 Byte) oder Int64 (8 Byte), sondern auch in eine andere Anzahl von Bytes erfolgen. Vielen Dank.
Valery Rode
Dies ist überhaupt nicht universell einsetzbar ... es gibt immer ein 8-Byte-Array zurück, was viel zusätzliche Arbeit mit Eingabewerten bringt, die keine 64-Bit-Ints sind. Ich möchte logischerweise ein 2-Byte-Array beim Konvertieren eines Int16.
Nyerguds