Pourquoi fread mess avec mon ordre de byte?
Im essayant d'analyser un fichier bmp avec fread()
et quand je commence à analyser, il inverse l'ordre de mes octets.
typedef struct{
short magic_number;
int file_size;
short reserved_bytes[2];
int data_offset;
}BMPHeader;
...
BMPHeader header;
...
Les données hex est 42 4D 36 00 03 00 00 00 00 00 36 00 00 00
;
Je suis le chargement des données hex dans la structure par fread(&header,14,1,fileIn);
Mon problème est de savoir où le nombre magique doit être 0x424d //'BM'
fread() retourne les octets à 0x4d42 //'MB'
Pourquoi ne fread() faire et comment puis-je le réparer;
EDIT: Si je n'étais pas assez précis, j'ai besoin de lire l'ensemble du bloc de données hexadécimales dans la struct pas seulement le nombre magique. J'ai seulement pris le nombre magique comme un exemple.
source d'informationauteur Chase Walden
Vous devez vous connecter pour publier un commentaire.
Ce n'est pas la faute de
fread
mais de votre CPU, qui est (apparemment) little-endian. C'est, votre PROCESSEUR traite le premier octet dans uneshort
valeur que la faible 8 bits, plutôt que, comme vous semblez avoir prévu) les 8 bits.Lors de la lecture d'un format de fichier binaire, vous devez explicitement convertir le format de fichier de stockage de la CPU natif boutisme. Vous le faites avec ces fonctions:
Vous faire votre
fread
dans unuint8_t
tampon de la taille appropriée, puis vous copiez manuellement tous les octets de données sur votreBMPHeader
struct, la conversion nécessaire. Qui ressemblerait à quelque chose comme ceci:Vous ne pas supposons que le PROCESSEUR de l'endianness est le même que le format de fichier même si vous savez pour un fait que pour l'instant, ils sont les mêmes; vous rédigez les conversions de toute façon, de sorte que dans l'avenir de votre code fonctionne sans modification sur un CPU avec la face de l'endianness.
Vous pouvez rendre la vie plus facile pour vous-même en utilisant la largeur fixe
<stdint.h>
types, en utilisant des types non signés, à moins d'être en mesure de représenter les nombres négatifs est absolument nécessaire, et par pas en utilisant des entiers quand des tableaux de caractères fera. J'ai fait toutes ces choses, dans l'exemple ci-dessus. Vous pouvez voir que vous n'avez pas besoin s'embêter endian de conversion le nombre magique, parce que la seule chose que vous devez faire c'est de testermagic_number[0]=='B' && magic_number[1]=='M'
.De Conversion dans la direction opposée, d'ailleurs, ressemble à ceci:
De Conversion de 32-/64-peu quantités laissée en exercice.
Je suppose que c'est un endian question. c'est à dire Vous de mettre les octets
42
et4D
dans votreshort
valeur. Mais votre système est en little endian (j'aurais pu le mauvais nom), qui lit les octets (au sein d'un multi-byte integer) de gauche à droite au lieu de droite à gauche.Démontré dans le présent code:
Donne le résultat suivant
Donc, si vous voulez être portable, vous aurez besoin de détecter la endian-ness de votre système, puis de faire un octet shuffle, si nécessaire. Il y aura beaucoup d'exemples round internet de la permutation des octets.
Question suivante:
Cela est dû à la mémoire de problèmes d'alignement. 196662 est les octets
36 00 03 00
et 3 est les octets03 00 00 00
. La plupart des systèmes ont besoin de types commeint
etc pour ne pas être divisée en de multiples mémoirewords
. Si votre intuition vous pensez que votre structure est aménagé im mémoire comme:MAIS sur un système 32 bits qui signifie
files_size
a 2 octets dans le mêmeword
commemagic_number
et deux octets dans le prochainword
. La plupart des compilateurs ne se présente pas pour cela, de sorte que la façon dont la structure est présentée dans la mémoire est en fait comme:Donc, quand vous lisez votre flux d'octets dans le
36 00
qui se passe dans votre remplissage de la zone qui part de votre taille_fichier à obtenir le03 00 00 00
. Maintenant, si vous avez utiliséfwrite
pour créer ces données, il aurait été OK que les octets de remplissage ont été écrits. Mais si votre entrée est toujours dans le format que vous avez spécifié, il n'est pas approprié pour lire l'ensemble de la structure avec fread. Au lieu de cela, vous aurez besoin de lire chacun des éléments individuellement.L'écriture d'une structure dans un fichier est fortement non-portable -- il est plus sûr de ne pas essayer de le faire. À l'aide d'une structure comme ceci est garanti pour fonctionner seulement si a) la structure est à la fois écrit et lu comme un struct (jamais une séquence d'octets) et b), il est toujours à la fois écrites et lues sur le même (type de) de la machine. Non seulement il y a "endian" problèmes avec les Processeurs différents (qui est ce qu'il semble que vous avez rencontré), il y a aussi "l'alignement" des questions. Matériel différentes implémentations ont des règles différentes au sujet de placer des nombres entiers, même sur 2 octets ou même 4 octets ou même 8-les limites d'octets. Le compilateur est pleinement conscient de tout cela, et insère caché des octets de remplissage dans votre structure de sorte qu'il fonctionne toujours à droite. Mais comme un résultat de l'caché des octets de remplissage, il n'est pas du tout sûr de supposer une structure d'octets du sont énoncées dans la mémoire, comme vous pensez qu'ils sont. Si vous êtes très chanceux, vous travaillez sur un ordinateur qui utilise big-endian ordre des octets et n'a pas d'alignement des restrictions à tous, de sorte que vous pouvez jeter les structures directement sur les fichiers et qu'il fonctionne. Mais vous n'êtes probablement pas que de la chance -- certainement les programmes qui doivent être "portable" à différentes machines doivent éviter d'essayer de jeter les structures directement au-dessus de toute partie de n'importe quel fichier.