C # int en octet []


172

J'ai besoin de convertir un inten une byte[]seule façon de le faire est d'utiliser BitConverter.GetBytes(). Mais je ne sais pas si cela correspond à la spécification suivante:

Un entier signé XDR est une donnée 32 bits qui code un entier compris dans la plage [-2147483648,2147483647]. L'entier est représenté dans la notation du complément à deux. Les octets les plus et les moins significatifs sont respectivement 0 et 3. Les entiers sont déclarés comme suit:

La source: RFC1014 3.2

Comment puis-je effectuer une transformation int en octet qui satisferait la spécification ci-dessus?


Voilà une bonne question.
ChaosPandion

Réponses:


217

La RFC essaie simplement de dire qu'un entier signé est un entier normal de 4 octets avec des octets ordonnés de manière big-endian.

Maintenant, vous travaillez très probablement sur une machine little-endian et BitConverter.GetBytes()vous donnerez l' byte[]inverse. Vous pouvez donc essayer:

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

Pour que le code soit le plus portable, cependant, vous pouvez le faire comme ceci:

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

7
Ou utilisez ToArray. byte [] result = BitConverter.GetBytes (intValue) .Reverse (). ToArray ();
Lars Truijens

pourquoi la dernière ligne byte[] result = intBytes;? n'est pas intBytesdéjà le tableau que vous voulez?
derHugo

2
@derHugo C'est exact, resultest redondant dans ce morceau de code. Cependant, j'ai pensé qu'il était plus pédagogique de montrer explicitement au lecteur quel était le résultat que de supposer qu'il pouvait comprendre que le résultat est contenu dans une variable nommée intBytes. De plus, faire l'affectation est bon marché car il ne copie pas la mémoire ni n'alloue de nouvelle mémoire, il ajoute simplement un nouveau nom au tableau déjà alloué. alors pourquoi ne pas le faire?
paracycle

41

Voici une autre façon de le faire: comme nous le savons tous 1x octet = 8x bits et aussi, un entier "régulier" (int32) contient 32 bits (4 octets). Nous pouvons utiliser l'opérateur >> pour décaler les bits vers la droite (l'opérateur >> ne change pas la valeur.)

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
L'initialiseur de tableau et le xor (^) et les & 0xFFbits ne sont pas nécessaires.
dtb

6
C'est big-endian, donc MSB est stocké en premier, vous devez donc inverser vos indices.
Marcin Deptuła

7
Pouvons-nous ajouter un exemple de sens inverse? (octets retournés à l'entier)
jocull

1
J'utilise ceci à cause des performances. Si je ne me soucie pas de cette petite différence, j'opterai pour la réponse acceptée.
Intemporel du

2
Vous devez envelopper ce code dans un uncheckedbloc. Actuellement, cela ne fonctionne que si la vérification du dépassement d'entier est désactivée dans les paramètres du compilateur.
CodesInChaos

25

BitConverter.GetBytes(int) fait presque ce que vous voulez, sauf que l'endianisme est faux.

Vous pouvez utiliser la méthode IPAddress.HostToNetwork pour permuter les octets dans la valeur entière avant d'utiliser BitConverter.GetBytesou d'utiliser la classe EndianBitConverter de Jon Skeet . Les deux méthodes font la bonne chose (tm) en ce qui concerne la portabilité.

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

3

Quand je regarde cette description, j'ai le sentiment que cet entier xdr n'est qu'un entier "standard" big-endian, mais il est exprimé de la manière la plus obscure. La notation du complément de Two est mieux connue sous le nom de U2, et c'est ce que nous utilisons sur les processeurs d'aujourd'hui. L'ordre des octets indique qu'il s'agit d'une notation big-endian .
Donc, pour répondre à votre question, vous devez inverser les éléments de votre tableau (0 <--> 3, 1 <--> 2), car ils sont encodés en petit-boutiste. Juste pour être sûr, vous devez d'abord vérifier BitConverter.IsLittleEndiansur quelle machine vous utilisez.


3

Pourquoi tout ce code dans les exemples ci-dessus ...

Une structure avec une disposition explicite agit dans les deux sens et n'a aucun impact sur les performances.

Mise à jour: Puisqu'il y a une question sur la façon de gérer l'endianness, j'ai ajouté une interface qui illustre comment résumer cela. Une autre structure d'implémentation peut gérer le cas contraire

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
Comment utiliseriez-vous cela? et deuxièmement, obtiendriez-vous le même résultat même si vous exécutez ceci sur «big-endian» et sur «little-endian».
Peter

@Peter voici une mise à jour. Il devrait quand même mieux performer que les équipes
Sten Petrov

Ouaip. Fondamentalement, une union c ++ implémentée en tant que structs c #. C'est super rapide et c'est bon pour emballer et déballer toutes sortes de choses qui ne rentrent pas bien dans les solutions aux problèmes bitconverter / endian. IIRC, NAudio utilise cette approche avec un très bon effet.
Craig

C'est la meilleure réponse et c'est un peu triste que ce ne soit pas le meilleur, mais vous dise juste quelque chose sur l'état de la programmation en 2019
John Evans


1

L'autre façon est d'utiliser BinaryPrimitives comme ceci

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

s'il vous plaît ajouter plus d'explications
Gregor Doroschenko

Merci pour la réponse, pourquoi avez-vous choisi d'écrire une nouvelle implémentation au lieu d'utiliser stackoverflow.com/a/1318948/58553 ? (y a-t-il des avantages ou des inconvénients avec votre solution)
Peter

Dans le cas où le projet doit être développé avec non seulement une langue, il peut être utile de réaliser la similitude de code entre les langues dans une situation où des idées identiques doivent être mises en œuvre. Cette méthode peut aider à détecter les erreurs. Bien sûr, en utilisant la fonction 'c #' 'BitConverter.GetBytes' avec 'Endian' vérifiant que tout ce dont vous avez besoin dans la plupart des cas. De plus, dans certaines situations, la conversion peut se produire non seulement en Int32 (4 octets) ou Int64 (8 octets), mais en un autre nombre d'octets. Merci.
Valery Rode le

Ce n'est pas vraiment du tout utilisable universellement ... il renvoie toujours un tableau de 8 octets, ce qui apporte beaucoup de travail supplémentaire avec des valeurs d'entrée qui ne sont pas des entiers 64 bits. Je voudrais logiquement un tableau de 2 octets lors de la conversion d'un fichier Int16.
Nyerguds
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.