Lire fichier binaire dans un struct

voix
42

Je suis en train de lire des données binaires en utilisant C #. J'ai toutes les informations sur la mise en page des données dans les fichiers que je veux lire. Je suis capable de lire les données « morceau par morceau », à savoir obtenir les 40 premiers octets de données de conversion à une chaîne, obtenir les 40 octets suivants.

Comme il y a au moins trois version légèrement différente des données, je voudrais lire les données directement dans un struct. On se sent tellement plus juste que 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 dont j'ai commencé à lire. Je reçois un AccessViolationException lors de l' utilisation Marshal.PtrToStructure.

Le flux contient plus d'informations que je suis en train de lire depuis que je ne suis pas intéressé par les données à la fin du fichier.

Le struct est défini 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 passe d'origine pour rendre cette question plus courte.

Comment puis-je lire les données binaires à partir d'un fichier dans un struct?

Créé 05/08/2008 à 15:28
source utilisateur
Dans d'autres langues...                            


7 réponses

voix
19

Le problème est la chaîne s dans votre struct. J'ai trouvé que les types marshaling comme octet / short / int est pas un problème; mais quand vous devez rassembler dans un type complexe, comme une chaîne, vous avez besoin de votre struct pour imiter explicitement un type non géré. Vous pouvez le faire avec le attrib MarshalAs.

Pour 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;
}
Créé 21/08/2008 à 20:02
source utilisateur

voix
8

Voici ce que je me sers.
Cela a fonctionné pour moi avec succès pour la lecture Format Portable Executable.
Il est une fonction générique, donc Test votre structgenre.

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;
}
Créé 02/11/2010 à 03:40
source utilisateur

voix
5

Comme Ronnie dit, j'utiliser BinaryReader et lu individuellement chaque champ. Je ne peux pas trouver le lien vers l'article avec cette information, mais il a été observé que l'utilisation BinaryReader pour lire chaque champ peut être plus rapide que Marshal.PtrToStruct, si la struct contient moins de 30-40 ou si les champs. Je vais poster le lien vers l'article quand je trouve.

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

Lorsque marshaling un tableau de struct, PtrToStruct gagne la haute main plus rapidement, parce que vous pouvez penser au nombre de champs comme des champs * longueur du tableau.

Créé 06/05/2010 à 02:04
source utilisateur

voix
3

J'ai eu pas de chance en utilisant BinaryFormatter, je suppose que je dois avoir un struct complet qui correspond au contenu du fichier exactement. J'ai réalisé qu'à la fin je ne suis pas intéressé à très grande partie du contenu du fichier de toute façon, donc je suis allé avec la solution de lecture partie du flux dans un ByteBuffer puis le convertir en utilisant

Encoding.ASCII.GetString()

pour les chaînes et

BitConverter.ToInt32()

pour les entiers.

Je dois être capable d'analyser plus du fichier plus tard, mais pour cette version que je suis parti avec seulement quelques lignes de code.

Créé 06/08/2008 à 10:03
source utilisateur

voix
1

Je ne vois pas de problème avec votre code.

juste hors de ma tête, 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 utilisez tampon [] dans votre BinaryReader au lieu de lecture des données de FileStream pour voir si vous obtenez toujours exception AccessViolation.

J'ai eu pas de chance en utilisant BinaryFormatter, je suppose que je dois avoir un struct complet qui correspond au contenu du fichier exactement.

Cela est logique, BinaryFormatter a son propre format de données, tout à fait incompatible avec le vôtre.

Créé 05/08/2008 à 16:31
source utilisateur

voix
0

La lecture directement dans struct est le mal - beaucoup un programme C a diminué au cours du fait de différents ordres d'octets, différentes implémentations du compilateur de champs, d'emballage, la taille des mots .......

Vous êtes le meilleur de l'octet de sérialisation et deserialising par octet. Utilisez la construction dans des choses si vous voulez ou tout simplement vous habituer à BinaryReader.

Créé 23/09/2008 à 22:43
source utilisateur

voix
0

Essaye ça:

using (FileStream stream = new FileStream(fileName, FileMode.Open))
{
    BinaryFormatter formatter = new BinaryFormatter();
    StructType aStruct = (StructType)formatter.Deserialize(filestream);
}
Créé 05/08/2008 à 15:56
source utilisateur

Cookies help us deliver our services. By using our services, you agree to our use of cookies. Learn more