c# - Lire un fichier binaire dans une structure

Translate

J'essaye 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 souhaite lire. Je suis capable de lire les données "morceau par morceau", c'est-à-dire obtenir les 40 premiers octets de données en les convertissant en chaîne, obtenir les 40 octets suivants.

Puisqu'il existe au moins trois versions légèrement différentes des données, je voudrais lire les données directement dans une structure. Cela semble tellement plus juste qu'en le lisant "ligne par ligne".

J'ai essayé l'approche 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 un FileStream ouvert à partir duquel j'ai commencé à lire. Je reçois unAccessViolationException lors de l'utilisationMarshal.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é par rapport à l'original pour raccourcir cette question.

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

This question and all comments follow the "Attribution Required."

Toutes les réponses

Translate

Le problème est lechaînes dans votre structure. J'ai trouvé que le marshaling des types comme byte / short / int n'est pas un problème; mais lorsque vous avez besoin de marshaler dans un type complexe tel qu'une chaîne, vous avez besoin de votre structure pour imiter explicitement un type non managé. Vous pouvez le faire avec l'attribut MarshalAs.

Pour votre exemple, ce qui suit 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;
}
La source
Bradley Lee
Translate

Voici ce que j'utilise.
Cela a fonctionné avec succès pour moi pour la lecture de Portable Executable Format.
C'est une fonction générique, doncTest tonstructtype.

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;
}
La source
Translate

Comme Ronnie l'a dit, j'utiliserais BinaryReader et lisais chaque champ individuellement. Je ne trouve pas le lien vers l'article avec ces informations, mais il a été observé que l'utilisation de BinaryReader pour lire chaque champ individuel peut être plus rapide que Marshal.PtrToStruct, si la structure contient moins de 30 à 40 champs environ. Je publierai le lien vers l'article lorsque je le trouverai.

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

Lors du marshaling d'un tableau de structures, PtrToStruct prend le dessus plus rapidement, car vous pouvez considérer le nombre de champs comme des champs * longueur du tableau.

La source
Translate

Je n'ai pas eu de chance d'utiliser le BinaryFormatter, je suppose que je dois avoir une structure complète qui correspond exactement au contenu du fichier. J'ai réalisé qu'à la fin je n'étais pas intéressé par une grande partie du contenu du fichier de toute façon, alors j'ai opté pour la solution de lecture d'une partie du flux en un bytebuffer, puis de la conversion en utilisant

Encoding.ASCII.GetString()

pour les cordes et

BitConverter.ToInt32()

pour les entiers.

J'aurai besoin de pouvoir analyser davantage le fichier plus tard, mais pour cette version, je me suis débrouillé avec seulement quelques lignes de code.

La source
Translate

Je ne vois aucun problème avec votre code.

juste hors de ma tête, et si vous essayez de le faire manuellement? est-ce que ça marche?

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 utiliseztampon[]dans votre BinaryReader au lieu de lire les données de FileStream pour voir si vous obtenez toujours une exception AccessViolation.

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

Cela a du sens, BinaryFormatter a son propre format de données, totalement incompatible avec le vôtre.

La source
Translate

Essaye ça:

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

Lire directement dans les structures est mal - de nombreux programmes C sont tombés à cause de différents ordres d'octets, de différentes implémentations de champs de compilateur, de l'emballage, de la taille des mots .......

Vous êtes le meilleur pour sérialiser et désérialiser octet par octet. Utilisez le build in stuff si vous le souhaitez ou habituez-vous simplement à BinaryReader.

La source