Comment puis-je convertir un big-endian struct pour un petit-boutiste-struct?
J'ai un fichier binaire qui a été créé sur une machine unix. C'est juste un tas de documents écrits l'un après l'autre. L'enregistrement est défini à quelque chose comme ceci:
struct RECORD {
UINT32 foo;
UINT32 bar;
CHAR fooword[11];
CHAR barword[11];
UNIT16 baz;
}
Je suis à essayer de comprendre comment je pourrais lire et d'interpréter ces données sur une machine Windows. J'ai quelque chose comme ceci:
fstream f;
f.open("file.bin", ios::in | ios::binary);
RECORD r;
f.read((char*)&detail, sizeof(RECORD));
cout << "fooword = " << r.fooword << endl;
Je reçois un tas de données, mais ce n'est pas les données que j'attends. Je suis suspect que mon problème a à voir avec la endian différence des machines, donc je viens de demander à ce sujet.
Je comprends que plusieurs octets seront stockées dans little-endian sur windows et big-endian dans un environnement unix, et je le conçois. Pour les deux octets, 0x1234 sur windows sera 0x3412 sur un système unix.
Ne endianness affectent l'ordre des octets de la structure dans son ensemble, ou de chaque membre de la structure? Quelles démarches devrais-je prendre à la convertir en une structure créée sur un système unix à un qui a les mêmes données sur un système windows? Les liens qui sont plus en profondeur que de l'ordre des octets de quelques octets serait génial, trop!
OriginalL'auteur scottm | 2009-05-13
Vous devez vous connecter pour publier un commentaire.
Ainsi que la endian, vous devez être conscient de rembourrage différences entre les deux plates-formes. Surtout si vous avez de longueur impaire des tableaux de char et 16 bits, vous pouvez ainsi trouver différents nombres d'octets de remplissage entre certains éléments.
Edit: si la structure a été écrit sans l'emballage, alors il devrait être assez simple. Quelque chose comme ceci (non testé) code:
Puis, une fois que vous avez chargé la structure, il suffit de le remplacer chaque élément:
cela ne va pas vous aider si les données que vous lisez a déjà mou octets. FWIW, cela ne devrait pas arriver, sauf si le développeur du programme a été écrit remplit les structures, ce qui est tout simplement mauvais. Les structures doivent toujours être écrits champ par champ, dans les situations exactement comme ça.
J'ai la source de la définition de la structure (mais pas pour la lecture ou l'écriture) et il a aussi des pack = 1.
Cela fonctionne si l'élément contient une seule valeur. N'oubliez pas d'écrire une boucle si l'élément est, disons, un tableau, d'échanger endian pour toutes les valeurs.
OriginalL'auteur James Sutherland
En fait, endianness est une propriété du matériel sous-jacent, pas de l'OS.
La meilleure solution est de les convertir au standard lors de l'écriture de données -- Google pour "network byte order" et vous devriez trouver les méthodes pour ce faire.
Edit: voici le lien: http://www.gnu.org/software/hello/manual/libc/Byte-Order.html
Dans ce cas, vous devez découvrir le mécanisme exact qui a été utilisé, et d'écrire vos propres routines pour convertir (ou de les trouver en ligne). Notez, cependant, que, bien que l'écrivain "n'est pas de changer," - il mieux ne jamais déplacer vers une autre architecture ou il va changer, qu'il le veuille ou non.
OriginalL'auteur kdgregory
Ne pas lire directement dans la structure à partir d'un fichier! L'emballage peut être différent, vous avez à jouer avec pragma pack ou similaire compilateur spécifique des constructions. Trop peu fiable. Beaucoup de programmeurs s'en tirer avec cela, car leur code n'est pas compilé grand nombre d'architectures et de systèmes, mais cela ne veut pas dire que c'est OK de chose à faire!
Une bonne approche alternative est de lire l'en-tête, que ce soit, dans une mémoire tampon et d'analyser à partir de trois pour éviter les I/O de frais généraux dans les opérations atomiques comme la lecture d'un non signé de 32 bits entier!
La déclaration de parse_uint32 devrait ressembler à ceci:
C'est une abstraction très simple, il n'a pas de coût supplémentaire dans la pratique, de mettre à jour le pointeur ainsi:
Le plus tard formulaire permet nettoyeur de code pour l'analyse de la mémoire tampon; le pointeur est automatiquement mis à jour lorsque vous analysez de l'entrée.
De même, memcpy pourrait avoir une aide, quelque chose comme:
La beauté de ce type de régime est que vous pouvez avoir de l'espace de noms "little_endian" et "big_endian", alors vous pouvez le faire dans votre code:
Facile de passer endianess pour le même code, cependant, rarement besoin d'une fonctionnalité.. fichier formats ont généralement un fixe endianess de toute façon.
NE PAS faire abstraction de ce en classe avec des méthodes virtuelles; voudrais juste ajouter les frais généraux, mais n'hésitez pas à si oui incliné:
L'objet de lecteur serait évidemment que d'être une mince wrapper autour de pointeur. Le paramètre de taille serait le contrôle d'erreur, le cas échéant. Pas vraiment obligatoire pour l'interface per se.
Remarquez comment le choix de l'endianess ici a été fait au MOMENT de la COMPILATION (depuis que nous avons créer little_endian_reader objet), de sorte que nous invoquons la méthode virtuelle de surcharge pour pas particulièrement bonne raison, donc je n'irais pas avec cette approche. 😉
À ce stade, il n'y a pas vraiment de raison de garder les "fileformat struct" autour de, vous pouvez organiser les données à votre convenance, et pas nécessairement le lire dans n'importe quel spécifiques struct à tous; après tout, c'est juste des données. Lorsque vous lisez des fichiers comme des images, vous n'avez pas vraiment besoin de l'en-tête autour.. vous devriez avoir votre image conteneur qui est la même pour tous les types de fichiers, de sorte que le code pour lire un format spécifique doit juste lire le fichier, de les interpréter et de les reformater les données & magasin de la charge utile. =)
Je veux dire, est-ce look compliqué?
Le code peut regarder qu'agréable, et vraiment très peu de frais généraux! Si l'endianess est de même pour les fichiers et l'architecture, le code est compilé, le innerloop peut ressembler à ceci:
Qui pourrait être illégal sur certaines architectures, de sorte que l'optimisation peut être une Mauvaise Idée, et l'utilisation plus lent, mais plus robuste approche:
Sur un x86 qui permet de compiler en bswap ou mov, qui est relativement faible surcharge si la méthode est incorporé; le compilateur à insérer le "move" nœud dans le code intermédiaire, rien d'autre, qui est assez efficace. Si l'alignement est un problème de la lecture complète de décalage ou de la séquence peuvent être générées, outch, mais encore pas trop mal. Comparez-branche pourrait permettre l'optimisation, si le test de l'adresse LSB et voir si vous pouvez utiliser le fast ou slow version de l'analyse. Mais cela voudrait dire pénalité pour le test de tous les lire. Pourrait ne pas être en vaut la peine.
Ah, oui, nous sommes à la lecture des en-TÊTES et des trucs, je ne pense pas que c'est un goulot d'étranglement dans de trop nombreuses applications. Si certains codec est en train de faire quelques très SERRÉ innerloop, encore une fois, la lecture dans une mémoire tampon temporaire et de décodage à partir de là est bien conseillé. Même principe.. personne ne lit octet-à-temps à partir d'un fichier lors du traitement d'un grand volume de données. Eh bien, en fait, j'ai vu ce genre de code très souvent et la réponse habituelle à "pourquoi vous le faites", c'est que les systèmes de fichiers ne bloquer lit et que les octets venir à partir de la mémoire de toute façon, c'est vrai, mais ils passent par un appel profond de la pile qui est haut de surcharge pour obtenir un peu d'octets!
Encore, écrire l'analyseur de code une fois et utilisez zillion de fois -> victoire épique.
De lecture directement dans la structure à partir d'un fichier: NE le faites PAS, les gars!
OriginalL'auteur
Il affecte à chaque membre de façon indépendante, et non pas l'ensemble de la
struct
. Aussi, il n'affecte pas les choses comme des tableaux. Par exemple, il est tout à fait octets dans uneint
s stockés dans l'ordre inverse.PS. Cela dit, il pourrait y avoir une machine avec bizarre boutisme. Ce que je viens de dire s'applique à la plupart des machines utilisées (x86, ARM, PowerPC, SPARC).
Oui, absolument. Je veux dire qu'il n'affecte pas l'ordre des éléments dans un tableau. Chaque membre est évidemment traitée comme une variable simple.
OriginalL'auteur Mehrdad Afshari
Vous devez corriger l'endianess de chaque membre de plus d'un octet, individuellement. Les chaînes n'ont pas besoin d'être converti (fooword et barword), comme ils peuvent être vus comme des séquences d'octets.
Cependant, vous devez prendre soin d'un autre problème: aligmenent membres de votre structure. Fondamentalement, vous devez vérifier si sizeof(RECORD) est le même sur les deux systèmes unix et windows code. Les compilateurs généralement de fournir des pragmas de définir l'aligment vous souhaitez (par exemple, #pragma pack).
OriginalL'auteur Jem
Vous devez également tenir compte de l'alignement des différences entre les deux compilateurs. Chaque compilateur est autorisé à introduire de rembourrage entre les membres dans une structure la mieux adaptée à l'architecture. Si vous avez vraiment besoin de savoir:
C'est pourquoi la plupart des programmes (Que j'ai vu (qui doivent être plate-forme neutre)) sérialiser les données dans un flux de texte qui peut être lu facilement par la norme iostreams.
OriginalL'auteur Martin York
J'aime à mettre en œuvre un SwapBytes méthode pour chaque type de données que les besoins de l'échange, comme ceci:
Puis-je ajouter une fonction à la structure qui a besoin de l'échange, comme ceci:
Ensuite, vous pouvez modifier votre code qui lit (ou écrit) de la structure comme ceci:
À l'appui de différentes plates-formes, vous avez juste besoin d'avoir une plate-forme de mise en œuvre spécifiques de chaque ByteSwap surcharge.
OriginalL'auteur kevin42
Quelque chose comme cela devrait fonctionner:
OriginalL'auteur xian