2010-03-18 11 views
0

Dire que j'ai une collection d'octetsComment prendre une collection d'octets et en extraire des valeurs typées?

var bytes = new byte[] {0, 1, 2, 3, 4, 5, 6, 7}; 

et je veux tirer une valeur définie des octets comme un type géré, par exemple un ushort. Quel est un moyen simple de définir quels types résident à quel endroit de la collection et de retirer ces valeurs?

Une façon (laid) est d'utiliser System.BitConverter et un Queue ou byte[] avec un index et simplement itérer, par exemple:

int index = 0; 
ushort first = System.BitConverter.ToUint16(bytes, index); 
index += 2; // size of a ushort 
int second = System.BitConverter.ToInt32(bytes, index); 
index += 4; 
... 

Cette méthode est très, très fastidieux lorsque vous traitez avec beaucoup de ces structures!

Je sais qu'il y a le System.Runtime.InteropServices.StructLayoutAttribute qui me permet de définir les emplacements des types à l'intérieur d'une structure ou d'une classe, mais il ne semble pas y avoir moyen d'importer la collection d'octets dans cette structure. Si je pouvais en quelque sorte superposer la structure sur la collection d'octets et extraire les valeurs, ce serait idéal. Par exemple.

Foo foo = (Foo)bytes; // doesn't work because I'd need to implement the implicit operator 
ushort first = foo.first; 
int second = foo.second; 
... 
[StructLayout(LayoutKind.Explicit, Size=FOO_SIZE)] 
public struct Foo { 
    [FieldOffset(0)] public ushort first; 
    [FieldOffset(2)] public int second; 
} 

Des idées sur la façon d'y parvenir?

[EDIT: Voir aussi mon question on how to deal with the bytes when they are big endian.]

+0

Est-ce que tous les types qui vous intéressent sont des ensembles intégrales (ou "fixes" d'intégrales)? – gooch

+0

@gooch: Non, mais nous pourrions nous contenter de cela et écrire des méthodes d'aide pour quand nous avons des chaînes. – Pat

+0

Ok, cela fonctionne très bien pour les types intégraux. C'est un peu encombrant pour les tableaux de ces types, car ils doivent être désignés "fixes" (ce qui signifie essentiellement qu'il s'agit d'un pointeur vers le début de la mémoire). Nous n'avons pas été en mesure d'atteindre d'autres types, mais il existe un moyen de spécifier la taille de chaque champ, cela devient rapidement poilu. – gooch

Répondre

1

Nous l'avons fait un peu comme nous parlons directement au matériel via des octets sur série.

Compte tenu de la définition struct

[StructLayout(LayoutKind.Explicit, Size=FOO_SIZE)] 
public struct Foo { 
    [FieldOffset(0)] public ushort first; 
    [FieldOffset(2)] public int second; 
} 

Vous pouvez utiliser une classe comme celui-ci pour effectuer la conversion

public class ByteArrayToStruct<StructType> 
{ 
    public StructType ConvertToStruct(int size, byte[] thebuffer) 
    { 
     try 
     { 
      int theSize = size; 
      IntPtr ptr1 = Marshal.AllocHGlobal(theSize); 
      Marshal.Copy(thebuffer, 0, ptr1, theSize); 
      StructType theStruct = (StructType)Marshal.PtrToStructure(ptr1, typeof(StructType)); 
      Marshal.FreeHGlobal(ptr1); 
      return theStruct; 
     } 
     catch (Exception) 
     { 
      return default(StructType); 
     } 
    } 
} 

A l'inverse, vous pouvez aussi créer une liste à partir du tableau et faire quelque chose comme ce qui suit:

ushort first = BitConverter.ToInt16(myList.ToArray(), 0); 
myList.RemoveRange(0, sizeof(ushort)); 
[...] 

Cela consisterait essentiellement à conserver les données pertinentes à e "tête" de la liste, de sorte que vous n'aurez pas à suivre la position dans le tableau.

+0

Bien! C'est ce que je cherchais, au moins de mes tests préliminaires. – Pat

+0

Oh non, ça ne marche que pour les little-endian bytes (puisque ma machine hôte est little endian)! Avez-vous une solution de contournement pour prendre en big-endian bytes? Notez qu'il n'est pas suffisant d'inverser l'ensemble du tableau d'octets, puisque c'est chaque valeur qui a permuté l'endianness: par ex.de '{1, 0, 2, 0, 0, 0}', vous pouvez retirer un 'ushort' de' 1' et 'uint' de' 2', mais inverser les octets vous donnerait un 'ushort' de '0' et' uint' de '0x00020001' ou' 131073'. – Pat

+0

Fondamentalement, j'ai besoin de la structure pour agir comme si elle était big-endian quand elle reçoit les octets, puis pour me permettre d'en extraire les valeurs comme si c'était l'endianness normal du code managé .NET. – Pat

0

faire la première manière. define une classe Buffer qui a les octets de données et un curseur. définir des méthodes telles que THEN GetInt16, GetInt32 etc. Ensuite, vous allez

Buffer b(bytes); 
    ushort a = b.getInt16(); 
    int x = b.getInt32(); 

J'ai dans mon sac utils. J'ai aussi le contraire, pour faire un tampon de chaînes ints, ....