web-dev-qa-db-fra.com

Comment obtenir des données little endian de big endian en c # en utilisant la méthode bitConverter.ToInt32?

je fais application dans c # .Dans cette application, j'ai tableau d'octets contenant des valeurs hexadécimales.

Ici, je reçois des données en tant que big endian mais je les veux en tant que petit endian.

Ici, j'utilise la méthode Bitconverter.toInt32 pour convertir cette valeur en entier.

Mais mon problème est qu'avant de convertir une valeur, je dois copier ces données de 4 octets dans un tableau temporaire à partir d'un tableau d'octets source, puis inverser ce tableau d'octets temporaire.

Je ne peux pas inverser le tableau source car il contient également d'autres données.

A cause de cela, mon application devient lente ... code ici J'ai un tableau source d'octets en tant que waveData []. Il contient beaucoup de données.

byte[] tempForTimestamp=new byte[4];
tempForTimestamp[0] = waveData[290];
tempForTimestamp[1] = waveData[289];
tempForTimestamp[2] = waveData[288];
tempForTimestamp[3] = waveData[287];
int number = BitConverter.ToInt32(tempForTimestamp, 0);

Existe-t-il une autre méthode pour cette conversion?

24
Dany

Si vous savez que les données sont big-endian, faites-le simplement manuellement:

int value = (buffer[i++] << 24) | (buffer[i++] << 16)
          | (buffer[i++] << 8) | buffer[i++];

cela fonctionnera de manière fiable sur n'importe quel processeur. Note i est votre décalage actuel dans la mémoire tampon.

Une autre approche serait de mélanger le tableau:

byte tmp = buffer[i+3];
buffer[i+3] = buffer[i];
buffer[i] = tmp;
tmp = buffer[i+2];
buffer[i+2] = buffer[i+1];
buffer[i+1] = tmp;
int value = BitConverter.ToInt32(buffer, i);
i += 4;

Je trouve le premier immensément plus lisible, et il n'y a pas de code complexe/branches, donc ça devrait marcher assez vite aussi. La seconde pourrait également rencontrer des problèmes sur certaines plates-formes (où le processeur exécute déjà le big-endian).

22
Marc Gravell

Dans Linq moderne, la version à une ligne et la plus facile à comprendre serait:

int number = BitConverter.ToInt32(waveData.Skip(286).Take(4).Reverse().ToArray(), 0);

Tu pourrais aussi ...

byte[] tempForTimestamp = new byte[4];
Array.Copy(waveData, 287, tempForTimestamp, 0, 4);
Array.Reverse(tempForTimestamp);
int number = BitConverter.ToInt32(tempForTimestamp);

:)

28
Allison A

Voici

public static int SwapEndianness(int value)
{
    var b1 = (value >> 0) & 0xff;
    var b2 = (value >> 8) & 0xff;
    var b3 = (value >> 16) & 0xff;
    var b4 = (value >> 24) & 0xff;

    return b1 << 24 | b2 << 16 | b3 << 8 | b4 << 0;
} 
11
Ioannis Karadimas

Déclarez cette classe:

using static System.Net.IPAddress;

namespace BigEndianExtension
{
    public static class BigEndian
    {
        public static short ToBigEndian(this short value)   => HostToNetworkOrder(value);
        public static int   ToBigEndian(this int value)     => HostToNetworkOrder(value);
        public static long  ToBigEndian(this long value)    => HostToNetworkOrder(value);
        public static short FromBigEndian(this short value) => NetworkToHostOrder(value);
        public static int   FromBigEndian(this int value)   => NetworkToHostOrder(value);
        public static long  FromBigEndian(this long value)  => NetworkToHostOrder(value);
    }
}

Exemple, créez un formulaire avec un bouton et une zone de texte multiligne:

using BigEndianExtension;

private void button1_Click(object sender, EventArgs e)
{
    short int16 = 0x1234;
    int int32   = 0x12345678;
    long int64  = 0x123456789abcdef0;
    string text = string.Format("LE:{0:X4}\r\nBE:{1:X4}\r\n", int16, int16.ToBigEndian());

    text += string.Format("LE:{0:X8}\r\nBE:{1:X8}\r\n", int32, int32.ToBigEndian());
    text += string.Format("LE:{0:X16}\r\nBE:{1:X16}\r\n", int64, int64.ToBigEndian());
    textBox1.Text = text;
}
//Some code...
8
FRL

Ajoutez une référence à nuget System.Memory et utilisez BinaryPrimitives.ReverseEndianness ().

using System.Buffers.Binary;
number = BinaryPrimitives.ReverseEndianness(number);

Il supporte les entiers signés et non signés (octet/short/int/long).

3
Herman

Si vous n'avez plus jamais besoin de ce tableau temporaire inversé, vous pouvez simplement le créer lorsque vous passez le paramètre, au lieu de faire quatre assignations. Par exemple:

int i = 287;
int value = BitConverter.ToInt32({
    waveData(i + 3),
    waveData(i + 2),
    waveData(i + 1),
    waveData(i)
}, 0);
1
M Granja

Je n'aime pas BitConverter, car (comme l'a répondu Marc Gravell), il est censé s'appuyer sur l'endianisme du système, ce qui signifie que vous devez techniquement effectuer une vérification de l'endianité du système à chaque fois que vous utilisez BitConverter pour vous assurer de ne pas inverser le tableau. Et généralement, avec les fichiers sauvegardés, vous connaissez généralement l’endianisme que vous essayez de lire, et ce n'est peut-être pas la même chose. Vous pouvez simplement gérer des formats de fichiers avec des valeurs big-endian, comme par exemple des morceaux PNG.

À cause de cela, j'ai juste écrit mes propres méthodes pour cela, qui prennent un tableau d'octets, le décalage de lecture et la longueur de lecture comme arguments, ainsi qu'un booléen pour spécifier le traitement de finalité, et qui utilise le décalage de bits pour plus d'efficacité:

public static UInt64 ReadIntFromByteArray(Byte[] data, Int32 startIndex, Int32 bytes, Boolean littleEndian)
{
    Int32 lastByte = bytes - 1;
    if (data.Length < startIndex + bytes)
        throw new ArgumentOutOfRangeException("startIndex", "Data array is too small to read a " + bytes + "-byte value at offset " + startIndex + ".");
    UInt64 value = 0;
    for (Int32 index = 0; index < bytes; index++)
    {
        Int32 offs = startIndex + (littleEndian ? index : lastByte - index);
        value += (UInt64)(data[offs] << (8 * index));
    }
    return value;
}

Ce code peut gérer toute valeur comprise entre 1 et 8 octets, à la fois little-endian et big-endian. La seule petite particularité d'utilisation est que vous devez à la fois donner le nombre d'octets à lire, et avoir besoin de transtyper spécifiquement le résultat dans le type souhaité.

Exemple de code où je l'ai utilisé pour lire l'en-tête d'un type d'image propriétaire:

Int16 imageWidth = (Int16) ReadIntFromByteArray(fileData, hdrOffset, 2, true);
Int16 imageHeight = (Int16) ReadIntFromByteArray(fileData, hdrOffset + 2, 2, true);

Cela lira deux entiers 16 bits consécutifs d'un tableau, sous forme de valeurs little-endian signées. Vous pouvez bien sûr créer un tas de fonctions de surcharge pour toutes les possibilités, comme ceci:

public Int16 ReadInt16FromByteArrayLe(Byte[] data, Int32 startIndex)
{
    return (Int16) ReadIntFromByteArray(data, startIndex, 2, true);
}

Mais personnellement, je ne me suis pas soucié de ça.

Et voici la même chose pour écrire des octets:

public static void WriteIntToByteArray(Byte[] data, Int32 startIndex, Int32 bytes, Boolean littleEndian, UInt64 value)
{
    Int32 lastByte = bytes - 1;
    if (data.Length < startIndex + bytes)
        throw new ArgumentOutOfRangeException("startIndex", "Data array is too small to write a " + bytes + "-byte value at offset " + startIndex + ".");
    for (Int32 index = 0; index < bytes; index++)
    {
        Int32 offs = startIndex + (littleEndian ? index : lastByte - index);
        data[offs] = (Byte) (value >> (8*index) & 0xFF);
    }
}

La seule exigence ici est que vous devez convertir l'argument d'entrée en entier non signé de 64 bits lorsque vous le transmettez à la fonction.

0
Nyerguds

J'utilise les fonctions d'assistance suivantes

public static Int16 ToInt16(byte[] data, int offset)
{
    if (BitConverter.IsLittleEndian)
        return BitConverter.ToInt16(BitConverter.IsLittleEndian ? data.Skip(offset).Take(2).Reverse().ToArray() : data, 0);
    return BitConverter.ToInt16(data, offset);
}
public static Int32 ToInt32(byte[] data, int offset)
{
    if (BitConverter.IsLittleEndian)
        return BitConverter.ToInt32(BitConverter.IsLittleEndian ? data.Skip(offset).Take(4).Reverse().ToArray() : data, 0);
    return BitConverter.ToInt32(data, offset);
}
public static Int64 ToInt64(byte[] data, int offset)
{
    if (BitConverter.IsLittleEndian)
        return BitConverter.ToInt64(BitConverter.IsLittleEndian ? data.Skip(offset).Take(8).Reverse().ToArray() : data, 0);
    return BitConverter.ToInt64(data, offset);
}
0
Zapnologica