2017-10-03 5 views
4

Pour ma première question ici, Je voudrais parler de la lecture de fichiers binaires en C++; Je recode une bibliothèque de balises ID3.C++, comportement bizarre lors de la lecture binaire ifstream

J'analyse l'en-tête qui est un fichier binaire, les premiers 10bytes sont les suivantes:

ID3 = 3 bytes = constant identifier 
0xXXXX = 2 bytes = version (MSB: major version, LSB: minor. eg: 0x0301 = v3.1) 
0xXX = 1 byte = some flags 
4*0xXX = 4 bytes = size 

ici est le morceau de code pour traiter que:

char   id[4]; 
uint16_t  version; 
uint8_t  flags; 
uint32_t  size; 
std::ifstream _stream; 

_stream = std::ifstream(_filename, std::fstream::binary); 

_stream.read(id, 3); 
id[3] = 0; 
// process id 
_stream.read((char *)&version, 2); 
// process version 
_stream.read((char *)&flags, 1); 
// process flags 
_stream.read((char*)&size, 4); 
// process flags 
_stream.close(); 

tout fonctionne bien sauf pour la version. disons que c'est v3.0 (0x0300), la valeur définie dans la version est 0x03, je comprendrais ce comportement en mode texte car il considérerait 0x00 comme la fin de la chaîne, mais ici je lis en binaire. Et utilisez des formats numériques.

Autre chose étrange, si je le processus en 2 fois, je peux le faire fonctionner, par exemple:

uint16_t version = 0; 
char  buff; 

_stream.read(&buff, 1); 
version = (buff << 8); 
_stream.read(&buff, 1); 
version |= buff; 

Dans ce cas, la valeur de la version est 0x0300.

Avez-vous une idée de la raison pour laquelle la première méthode ne fonctionne pas correctement? Est-ce que je fais quelque chose de mal?

Quoi qu'il en soit, merci pour votre aide,

Cheers!

+7

Voici quelques aliments google pour vous: « little endian » et « grand endian ". –

+0

Vous devez d'abord définir précisément votre format de fichier (peut-être en notation EBNF) –

+0

En passant, si vous cherchez un code indépendant de la plate-forme, il n'y a aucune garantie qu'un octet est 8 bits (ces mêmes plates-formes où cas probablement aussi ne supporterait pas non plus les types entiers à largeur fixe) – AndyG

Répondre

4

Le champ version ne comprend pas un court non signé mais deux octets non signés (version majeure, version secondaire). Vous devriez lire les deux numéros de version séparément pour ne pas vous retrouver dans les problèmes d'endianess.

Endianess est spécifique à la plate-forme. Si vous insistez pour lire un seul court qui combine la version majeure et la version mineure, vous pouvez contourner ce problème. Mais à la fin, vous écrivez un code moins propre et compréhensible pour résoudre un problème que vous avez créé vous-même.

+0

@HWalters Il existe des outils pour convertir des nombres dans un flux d'un endianess donné vers la plate-forme locale, par ex. 'ntohs()' et semblables. Vous pouvez donc obtenir un court-métrage contenant à la fois une version majeure et une version mineure indépendamment de la plate-forme. Cela ne vaut tout simplement pas la peine dans cet exemple, comparé à la simple lecture indépendante des deux nombres. – ypnos

+0

@ypnos, en fait vous avez raison, j'ai fini par lire octet par octet, c'est beaucoup plus simple et plus facile à lire. mais la façon dont il est écrit dans les spécifications, je ne comprenais pas c'était deux octets séparés, je pensais que j'étais l'un. –

1

Cela semble être un problème d'endianess. Alors c'est quoi? Selon Wikipedia:

Endianness se réfère à l'ordre séquentiel dans lequel les octets sont disposés en valeurs numériques plus grandes, lorsqu'il est stocké dans la mémoire de l'ordinateur ou le stockage secondaire

exemple visuel d'une mise en page en mémoire:

big-little-endian

Image origin

Lorsque vous lisez la valeur en une fois, les octets sont réarrangés, probablement en raison d'une incohérence entre la façon dont ils ont été écrits et la façon dont ils sont lus.

Puisque vous connaissez l'ordre dans lequel ils pondent en mémoire, vous devez faire une des opérations suivantes:

  1. octet par octet de lecture.
  2. Lire la valeur et échanger les octets en utilisant _byteswap_ushort dans VC++ ou __builtin_bswap16 pour GCC
  3. Lire la valeur et échanger les octets en utilisant un custom implementation
+0

Ne faites pas # 2. Il n'est absolument pas nécessaire d'utiliser une extension spécifique au fournisseur ici et de rendre votre code non portable. –

+1

@underscore_d, ajout d'une référence aux implémentations de swap personnalisées –

+0

@DanielTrugman Cela me rappelle les cours de la journée à l'école, comment ai-je manqué ça ... Je me serais giflé pour cette erreur il y a 5 ans. Je pense que j'ai passé trop de temps sur des langages de haut niveau. De toute façon, merci pour la réponse, j'ai fini par le lire byte by byte –