Lire le fichier binaire dans une structure

j'essaie de lire des données binaires en utilisant C#. J'ai toutes les informations sur la disposition des données dans les fichiers que je veux lire. Je suis capable de lire les données "chunk par chunk", c.-à-d. obtenir les 40 premiers octets de données la convertissant en une chaîne, obtenir les 40 octets suivants.

Puisqu'il y a au moins trois version légèrement différente des données, je voudrais lire les données directement dans une struct. Il se sent tellement plus juste qu'en le lisant " ligne par ligne."

j'ai essayé la méthode suivante, mais en vain:

StructType aStruct;
int count = Marshal.SizeOf(typeof(StructType));
byte[] readBuffer = new byte[count];
BinaryReader reader = new BinaryReader(stream);
readBuffer = reader.ReadBytes(count);
GCHandle handle = GCHandle.Alloc(readBuffer, GCHandleType.Pinned);
aStruct = (StructType) Marshal.PtrToStructure(handle.AddrOfPinnedObject(), typeof(StructType));
handle.Free();

Le flux est ouvert FileStream à partir duquel j'ai commencé à lire à partir de. Je reçois un AccessViolationExceptio n en utilisant Marshal.PtrToStructure.

le flux contient plus d'informations que j'essaie de lire car je ne suis pas intéressé par les données à la fin du fichier.

La structure est définie comme:

[StructLayout(LayoutKind.Explicit)]
struct StructType
{
    [FieldOffset(0)]
    public string FileDate;
    [FieldOffset(8)]
    public string FileTime;
    [FieldOffset(16)]
    public int Id1;
    [FieldOffset(20)]
    public string Id2;
}

le code des exemples est modifié de original de faire de cette question plus en plus courte.

Comment pourrais-je lire les données binaires d'un fichier dans une structure?

44
demandé sur EM-Creations 2008-08-05 18:28:57

7 réponses

Le problème est le chaînes dans votre structure. J'ai trouvé que les types de marshaling comme byte/short/int ne sont pas un problème; mais quand vous avez besoin de marshal dans un type complexe tel qu'une chaîne de caractères, vous avez besoin de votre struct pour imiter explicitement un type non géré. Vous pouvez le faire avec L'atrib MarshalAs.

Pour ton exemple, la suivante devrait fonctionner:

[StructLayout(LayoutKind.Explicit)]
struct StructType
{
    [FieldOffset(0)]
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 8)]
    public string FileDate;

    [FieldOffset(8)]
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 8)]
    public string FileTime;

    [FieldOffset(16)]
    public int Id1;

    [FieldOffset(20)]
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 66)] //Or however long Id2 is.
    public string Id2;
}
23
répondu Ishmaeel 2008-08-21 19:02:50

Voici ce que j'utilise.

cela a fonctionné avec succès pour moi pour la lecture Format exécutable Portable.

C'est une fonction générique, donc T votre struct type.

public static T ByteToType<T>(BinaryReader reader)
{
    byte[] bytes = reader.ReadBytes(Marshal.SizeOf(typeof(T)));

    GCHandle handle = GCHandle.Alloc(bytes, GCHandleType.Pinned);
    T theStructure = (T)Marshal.PtrToStructure(handle.AddrOfPinnedObject(), typeof(T));
    handle.Free();

    return theStructure;
}
9
répondu user3666197 2015-08-31 16:08:11

comme Ronnie l'a dit, J'utiliserais BinaryReader et je lirais chaque champ individuellement. Je ne peux pas trouver le lien à l'article avec cette information, mais il a été observé que L'utilisation BinaryReader pour lire chaque champ individuel peut être plus rapide que Marshal.PtrToStruct, si la structure contient moins de 30-40 champs. Je posterai le lien vers l'article quand je le trouverai.

Le lien est à: http://www.codeproject.com/Articles/10750/Fast-Binary-File-Reading-with-C

lors de la formation d'un tableau de structures, PtrToStruct gagne la main supérieure plus rapidement, parce que vous pouvez penser que le nombre de champs est la longueur du tableau fields*.

5
répondu nevelis 2012-09-01 17:34:05

Je n'ai pas eu de chance en utilisant le BinaryFormatter, je suppose que je dois avoir une structure complète qui correspond exactement au contenu du fichier. Je me suis rendu compte qu'à la fin je n'étais pas intéressé par beaucoup du contenu du fichier de toute façon donc je suis allé avec la solution de la lecture d'une partie de stream dans un bytebuffer et ensuite convertir en utilisant

Encoding.ASCII.GetString()

pour les cordes et les

BitConverter.ToInt32()

pour les nombres entiers.

je vais avoir besoin de pouvoir analyser plus du dossier plus tard, mais pour cette version, je m'en suis sorti avec juste quelques lignes de code.

3
répondu Robert Höglund 2012-01-07 00:53:09

Je ne vois aucun problème avec votre code.

juste sorti de ma tête, et si vous essayiez de le faire manuellement? cela fonctionne?

BinaryReader reader = new BinaryReader(stream);
StructType o = new StructType();
o.FileDate = Encoding.ASCII.GetString(reader.ReadBytes(8));
o.FileTime = Encoding.ASCII.GetString(reader.ReadBytes(8));
...
...
...

essayez aussi

StructType o = new StructType();
byte[] buffer = new byte[Marshal.SizeOf(typeof(StructType))];
GCHandle handle = GCHandle.Alloc(buffer, GCHandleType.Pinned);
Marshal.StructureToPtr(o, handle.AddrOfPinnedObject(), false);
handle.Free();

puis utilisez buffer [] dans votre lecteur BinaryReader au lieu de lire les données de FileStream pour voir si vous obtenez toujours L'exception AccessViolation.

Je n'ai pas eu de chance en utilisant le BinaryFormatter, je suppose que je dois avoir une structure complète qui correspond le contenu de le fichier exactement.

cela a du sens, BinaryFormatter a son propre format de données, complètement incompatible avec le vôtre.

1
répondu lubos hasko 2008-08-06 09:13:24

essaye ceci:

using (FileStream stream = new FileStream(fileName, FileMode.Open))
{
    BinaryFormatter formatter = new BinaryFormatter();
    StructType aStruct = (StructType)formatter.Deserialize(filestream);
}
0
répondu urini 2008-08-05 14:56:10

lire directement dans les structures est mauvais - beaucoup de programmes C sont tombés en panne à cause de différentes commandes d'octets, différentes implémentations de compilateurs de champs, l'emballage, la taille des mots.......

il est préférable de sérialiser et de desérialiser byte byte. Utilisez la construction dans les trucs si vous voulez ou juste s'habituer à BinaryReader.

0
répondu Ronnie 2008-09-23 21:43:29