2017-10-03 8 views
4

Für meine erste Frage hier, Ich möchte über das Lesen von Binärdateien in C++ sprechen; Ich rekodiere eine ID3-Tag-Bibliothek.C++, seltsames Verhalten beim Lesen binärer ifstream

Ich bin Parsen der Header, der eine binäre Datei, die ersten 10bytes sind wie folgt:

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 

hier ist das Stück Code, das zu verarbeiten:

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(); 

funktioniert alles einwandfrei außer für Version. lässt sagen, es ist v3.0 (0x0300), der Wert in der Version ist 0x03 eingestellt, würde ich dieses Verhalten im Textmodus verstehen, da es 0x00 als Ende der Zeichenfolge betrachten würde, aber hier bin ich in Binär lesen. Und verwende numerische Formate.

Andere seltsame Sache, wenn ich es in 2 mal verarbeiten kann ich es funktioniert, zum Beispiel:

uint16_t version = 0; 
char  buff; 

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

In diesem Fall wird der Wert der Version 0x0300 ist.

Haben Sie eine Idee, warum die erste Methode nicht richtig funktioniert? Mache ich etwas falsch?

Sowieso Dank für Ihre Hilfe,

Prost!

+7

mit: "Little-Endian" und „big Endian ". –

+0

Zuerst müssen Sie genau Ihr Dateiformat definieren (vielleicht in der EBNF-Notation). –

+0

Wenn Sie nach plattformunabhängigem Code suchen, gibt es keine Garantie, dass ein Byte 8 Bit ist (die gleichen Plattformen wie das Case würde wahrscheinlich auch keine Integer-Typen mit fester Breite unterstützen. – AndyG

Antwort

4

Das Versionsfeld besteht nicht aus einem vorzeichenlosen Kurzzeichen, sondern aus zwei vorzeichenlosen Bytes (Hauptversion, Nebenversion). Sie sollten die beiden Versionsnummern separat lesen, um nicht in Endanfälligkeitsproblemen gestört zu werden.

Endianess ist plattformspezifisch. Wenn Sie darauf bestehen, einen einzigen kurzen Text zu lesen, der Haupt- und Nebenversion kombiniert, könnten Sie damit umgehen. Aber am Ende schreiben Sie weniger sauberen und verständlichen Code, um ein Problem zu lösen, das Sie selbst erstellt haben.

+0

@HWalters Es gibt Werkzeuge, um Zahlen in einem Bytestream einer gegebenen Endianess auf die lokale Plattform, z. 'ntohs()' und gleich. Sie könnten also eine Kurzversion erhalten, die sowohl die Haupt- als auch die Nebenversion plattformunabhängig enthält. In diesem Beispiel lohnt es sich nicht, beide Zahlen unabhängig voneinander zu lesen. – ypnos

+0

@ypnos, eigentlich hast du recht, ich habe Byte für Byte gelesen, es ist viel einfacher und einfacher zu lesen. aber die Art, wie es in den Spezifikationen geschrieben wird, habe ich nicht verstanden es war zwei getrennte Bytes, ich dachte, ich wäre einer. –

1

Dies scheint wie ein endianess Problem. Also, was ist es? Nach Wikipedia:

Endianness bezieht sich auf die sequentielle Reihenfolge, in der Bytes in größere Zahlenwerte angeordnet sind, wenn sie in einem Computerspeicher oder Sekundärspeicher

visuelles Beispiel eines Layouts im Speicher gespeichert:

big-little-endian

Image origin

Wenn Sie den Wert als One-Shot-Datei lesen, werden die Bytes möglicherweise aufgrund einer Inkonsistenz zwischen der Schreibweise und der Lesart neu angeordnet.

Da Sie die Reihenfolge kennen, in der sie im Speicher legen, sollten Sie eine der folgenden Aktionen ausführen:

  1. lesen Byte für Byte.
  2. Lesen Sie den Wert und tauschen die Bytes _byteswap_ushort in VC++ oder __builtin_bswap16 für GCC
  3. den Wert lesen und die Bytes tauschen ein Hier einige Google-Essen für Sie custom implementation
+0

Tun Sie nicht # 2. Es ist absolut nicht notwendig, hier eine herstellerspezifische Erweiterung zu verwenden und Ihren Code nicht portierbar zu machen. –

+1

@underscore_d, hinzugefügt einen Verweis auf benutzerdefinierte Swap-Implementierungen –

+0

@DanielTrugman Es erinnert mich Klassen zurück in den Tag in der Schule, wie habe ich das vermisst ... Ich hätte mich für diesen Fehler vor 5 Jahren geschlagen. Ich glaube, ich habe zu viel Zeit in Hochsprachen verbracht. Wie auch immer, danke für die Antwort, ich habe es Byte für Byte gelesen –