6
votes

C #, lire des structures de fichier binaire

Je veux lire des structures de binaire. En C ++, je le ferais comme ceci: XXX

Y a-t-il une manière similaire dans C #? Le BinaryReader ne fonctionne que pour des types intégrés. En .NET 4 Il y a un MemoryPappeViewAccessor. Il fournit des méthodes telles que lire qui semble être ce que je veux, sauf que je dois manuellement à garder une trace de l'endroit où je veux lire dans le fichier. Y a-t-il une meilleure façon?


3 commentaires

Cela dépend de la façon dont ils ont été écrits


codeProject.com/kb/cs/objserial.aspx


code.google.com/p/protobuf-net


5 Réponses :


1
votes

Il n'y a pas de manière similaire dans C #. De plus, il s'agit d'une manière obsolète de sérialiser en raison de sa non-portabilité. Utilisez http://www.codeproject.com/kb/cs/objserial.aspx à la place.


5 commentaires

Dans quel cas il n'est-il pas portable? J'utilise ce code C ++ pour lire les mêmes données en X86 et X64 et semble fonctionner correctement.


Si vous écrivez les données sur une plate-forme (x86, par exemple) et lisez dans un autre (64), vous pouvez avoir des problèmes.


C'est exactement ce que je ne comprends pas, parce que je le fais. Avez-vous peut-être un lien avec quelque chose d'expliquer la question dans un peu plus de détails?


Non, je n'ai pas de lien avec une explication complète, désolé. Mais je sais que la mise en page de la mémoire diffère dans différentes plates-formes et même lorsque différents arguments du compilateur ont été utilisés. Il n'y a pas de standard exact que "les champs en mémoire doivent apparaître dans le même ordre que dans le code source" ou "longs sont représentés par 32 bits sur toutes les plateformes" ou "les champs sont alignés dans des paquets 32 bits partout" et il ne peut pas pu être un tel standard. Succès en utilisant ce code C ++ en X86 et en X64 signifie que vous avez juste chanceux. Essayez de jouer avec les clés du compilateur ou essayez de compiler pour le bras.


Il a tendance à être plus d'une mise en œuvre du compilateur plutôt que x86 vs x64.



2
votes

Il est possible de faire quelque chose de similaire dans C #, mais vous devrez ensuite appliquer beaucoup d'attributs à une structure afin de contrôler exactement comment il est aménagé en mémoire. Par défaut, le compilateur JIT contrôle comment les membres de la structure sont aménagés en mémoire, ce qui signifie généralement qu'ils sont réarrangés et rembourrés pour la disposition la plus efficace en considérant la vitesse et l'utilisation de la mémoire.

Le moyen le plus simple est généralement d'utiliser le BinaryReader pour lire les membres distincts de la structure dans le fichier et mettre les valeurs dans les propriétés d'une classe, c'est-à-dire désériorialiser manuellement les données dans une instance de classe.

Normalement, il lit le fichier qui est le cou de la bouteille dans cette opération, de sorte que la petite générale de la lecture des membres distinctes n'affecte pas la performance visiblement.


3 commentaires

Semble raisonnable. La performance n'est pas le problème principal ici, je pensais juste que c'est un peu gênant.


En y pensant un peu plus, je ne veux pas utiliser une boucle, juste pour lire un tableau de quelque chose.


@B_old: Il est beaucoup plus facile d'écrire les quelques lignes de code pour lire la valeur une à la fois, que d'obtenir les attributs à droite pour tous les membres d'une structure afin de garantir la garantie exactement en mémoire que le fichier est arrangé. Vous ne vous échapperez pas d'utiliser une boucle dans une forme de la solution que vous choisissez.



15
votes
SomeStruct s = stream.ReadStruct<SomeStruct>();

5 commentaires

@jesperll: C'est une très mauvaise idée, surtout si la structure n'est pas plate. S'il y a des pointeurs n'importe où dans la structure, cette structure / classe référencée ne sera pas écrite à la sortie. Pire encore, lorsqu'il est lu, il indiquera un espace mémoire non valide.


Vrai. Vous rencontrez des problèmes si vous avez des trucs comme des tableaux dans votre structure car ils ne sont pas des types de valeur


Mais si vous l'utilisez pour analyser un format de fichier dans lequel il s'agit de blocs d'en-tête avec des types simples, il est assez faisable


+1 Ceci est parfaitement viable, j'utilise à peu près le même code de lecture dans des structures binaires à partir de fichiers ASF - @casperone Je ne pense pas que la question demandait à des mécanismes complexes de sérialisation / de désérialisation de l'objet


Très cool! Marshal.ptrtostructure () Jette sur les types d'énumjets (éventuellement parce que vous ne pouvez pas utiliser [structlayout] sur Enum?). Pour Enums, vous pouvez utiliser typeof (t) .getenumunderlyingtype () et ça marche.



1
votes

Juste pour élaborer la réponse de Guffa et de Jesperll, voici un exemple de lecture dans l'en-tête de fichier pour un fichier ASF (WMV / WMA) en utilisant fondamentalement la même méthode de lecture (juste pas comme méthode d'extension) xxx


0 commentaires

3
votes

Voici une version légèrement modifiée du code de Jesper:

public static T? ReadStructure<T>(this Stream stream) where T : struct
{
    if (stream == null)
        return null;

    int size = Marshal.SizeOf(typeof(T));
    byte[] bytes = new byte[size];
    if (stream.Read(bytes, 0, size) != size) // can't build this structure!
        return null;

    GCHandle handle = GCHandle.Alloc(bytes, GCHandleType.Pinned);
    try
    {
        return (T)Marshal.PtrToStructure(handle.AddrOfPinnedObject(), typeof(T));
    }
    finally
    {
        handle.Free();
    }
}


0 commentaires