61 votes

Lire un 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 "morceau par morceau", c'est-à-dire de récupérer les 40 premiers octets de données en les convertissant en une chaîne de caractères, puis de récupérer les 40 octets suivants.

Comme il existe au moins trois versions légèrement différentes des données, j'aimerais lire les données directement dans un struct. Cela me semble tellement plus juste que de les lire "ligne par ligne".

J'ai essayé l'approche suivante, mais sans succès :

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 un FileStream ouvert à partir duquel j'ai commencé à lire. J'obtiens un AccessViolationExceptio n lors de l'utilisation de Marshal.PtrToStructure .

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

La structure est définie comme suit :

[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 a été modifié par rapport à l'original pour rendre cette question plus courte.

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

0voto

urini Points 8233

Essayez ça :

using (FileStream stream = new FileStream(fileName, FileMode.Open))
{
    BinaryFormatter formatter = new BinaryFormatter();
    StructType aStruct = (StructType)formatter.Deserialize(filestream);
}

0voto

Ronnie Points 3742

La lecture directe des structs est un mal - de nombreux programmes C ont échoué à cause de l'ordre différent des octets, de l'implémentation différente des champs par le compilateur, de l'empaquetage, de la taille des mots........

La meilleure façon de sérialiser et désérialiser est de le faire octet par octet. Utilisez les fonctions intégrées si vous le souhaitez ou habituez-vous simplement à BinaryReader.

0voto

ssamko Points 516

J'avais une structure :

[StructLayout(LayoutKind.Explicit, Size = 21)]
    public struct RecordStruct
    {
        [FieldOffset(0)]
        public double Var1;

        [FieldOffset(8)]
        public byte var2

        [FieldOffset(9)]
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 12)]
        public string String1;
    }
}

et j'ai reçu "incorrectement aligné ou recouvert par un non-objet" . Sur cette base, j'ai trouvé : https://social.msdn.microsoft.com/Forums/vstudio/en-US/2f9ffce5-4c64-4ea7-a994-06b372b28c39/strange-issue-with-layoutkindexplicit?forum=clr

OK. Je crois que je comprends ce qui se passe ici. Il semble que le problème est lié au fait que le type tableau (qui est un type d'objet) doit être ) doit être stocké à une frontière de 4 octets dans la mémoire. Cependant, ce que ce que vous essayez vraiment de faire est de sérialiser les 6 octets séparément.

Je pense que le problème est le mélange entre FieldOffset et la sérialisation de sérialisation. Je pense que structlayout.sequential peut fonctionner pour vous, puisqu'il ne modifie pas la représentation en mémoire de la structure. structure. Je pense que FieldOffset modifie en fait la représentation en mémoire du type. Cela pose des problèmes car le cadre .NET exige que les références d'objets soient alignées sur des limites appropriées (il semble). semble).

Ainsi, ma structure a été définie comme explicite avec :

[StructLayout(LayoutKind.Explicit, Size = 21)]

et donc mes champs avaient spécifié

[FieldOffset(<offset_number>)]

mais lorsque vous changez votre structure en structure séquentielle, vous pouvez vous débarrasser de ces décalages et l'erreur disparaîtra. Quelque chose comme :

[StructLayout(LayoutKind.Sequential, Size = 21)]
    public struct RecordStruct
    {
        public double Var1;

        public byte var2;

        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 12)]
        public string String1;
    }
}

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X