2016-06-11 13 views
1

Ich habe eine Variable vom Typ uint8_t, die ich serialisieren und in eine Datei schreiben möchte (was ziemlich portabel sein sollte, zumindest für Windows, was ich anstrebe) .Umwandlung eines uint8_t in seine binäre Darstellung

in einer Datei in seiner binären Form schreiben Der Versuch, kam ich accross diesem Arbeits Schnipsel:

uint8_t m_num = 3; 
unsigned int s = (unsigned int)(m_num & 0xFF); 
file.write((wchar_t*)&s, 1); // file = std::wofstream 

Lassen Sie mich zunächst sicher, ich verstehe, was diese Schnipsel tut - es ist mein var nimmt (Das ist im Grunde ein Zeichen ohne Vorzeichen (1 Byte lang), wandelt es in ein unsigned int um (das 4 Byte lang und nicht so tragbar ist) und & 0xFF"Extrakte" nur das niedrigstwertige Byte.

Nun gibt es zwei Dinge, die ich nicht verstehe:

  1. Warum es in unsigned int in erster Linie konvertieren, warum kann ich nicht einfach etwas tun, wie
    file.write((wchar_t*)&m_num, 1); oder reinterpret_cast<wchar_t *>(&m_num)? (Ref)
  2. Wie würde ich einen längeren Typ serialisieren, sagen Sie eine uint64_t (die 8 Bytes lang ist)? unsigned int kann oder kann hier nicht genug sein.
+2

Dieser Code ist schrecklich, nicht tragbar und hat undefiniertes Verhalten. Es zielt auch auf einen Strom von breiten Zeichen, die Sie wahrscheinlich nicht sind. –

+0

Hi @AlanStokes, danke für deinen Kommentar. Können Sie bitte erläutern, warum dieser Code schlecht und nicht tragbar ist? Was wäre ein weiserer Weg, das zu tun? – Asaf

Antwort

1

uint8_t ist 1 Byte, gleich wie char

wchar_t 2 Bytes in Windows ist, 4 Bytes in Linux. Es hängt auch von Endianess ab. Sie sollten vermeiden, wchar_t, wenn die Portabilität ein Anliegen ist.

Sie können einfach std::ofstream verwenden. Windows hat eine zusätzliche Version für std::ofstream, die UTF16-Dateinamen akzeptiert. Auf diese Weise ist Ihr Code kompatibel mit Windows UTF16-Dateinamen und Sie können weiterhin std::fstream verwenden. Zum Beispiel

int i = 123; 
std::ofstream file(L"filename_in_unicode.bin", std::ios::binary); 
file.write((char*)&i, sizeof(i)); //sizeof(int) is 4 
file.close(); 
... 
std::ifstream fin(L"filename_in_unicode.bin", std::ios::binary); 
fin.read((char*)&i, 4); // output: i = 123 

Dies ist relativ einfach, weil es nur ganze Zahlen speichert. Dies funktioniert auf verschiedenen Windows-Systemen, weil Windows immer little-endian ist, und int Größe ist immer 4.

Aber einige Systeme sind Big-Endian, müssten Sie separat damit umgehen.

Wenn Sie Standard-E/A verwenden, zum Beispiel fout << 123456, wird die ganze Zahl als Text "123456" gespeichert. Standard-I/O ist kompatibel, benötigt aber etwas mehr Speicherplatz und kann etwas langsamer sein.

Es ist Kompatibilität versus Leistung. Wenn Sie große Datenmengen (mehrere Megabyte oder mehr) haben und in Zukunft mit Kompatibilitätsproblemen umgehen können, sollten Sie mit dem Schreiben von Bytes fortfahren. Ansonsten ist es einfacher, Standard-I/O zu verwenden. Der Leistungsunterschied ist normalerweise nicht messbar.

+0

Hallo @BarmakShemirani, danke für deine Antwort! Es macht jetzt sehr viel Sinn! Offensichtlich ist Char viel tragbarer, denke ich. Könnten Sie bitte erklären, warum es besser ist, Standard-I/O zu verwenden? und welche anderen Portabilitätsprobleme gibt es? – Asaf

+0

Ich fügte mehr Erklärung in der Antwort hinzu. Es gibt auch ein Problem mit Text. Wenn Sie Kompatibilität mit anderen Systemen wünschen, ist es üblich, UTF16 in UTF8 zu konvertieren. Ich weiß nicht, ob Sie Text in Ihre Datei einfügen, ich bin nicht dazu gekommen. –

0

Es ist unmöglich unit8_t Werte in wofstream zu schreiben, weil wofstream nur breite Zeichen schreibt und keine binären Werte verarbeitet.

Wenn Sie ein breites Zeichen für einen Codepunkt zwischen 0 und 255 schreiben möchten, ist Ihr Code korrekt.

Wenn Sie binäre Daten in eine Datei schreiben möchten, dann ist ofstream der nächstgelegene Äquivalent, mit dem Sie Bytes schreiben können.

Ihre Fragen zu beantworten:

  1. wofstream::write schreibt breite Zeichen, nicht Bytes. Wenn Sie die Adresse m_num als Adresse eines Wide-Zeichens neu interpretieren, werden Sie ein 16-Bit- oder 32-Bit-Zeichen (abhängig von der Plattform) schreiben, von dem das erste Byte (dh das am wenigsten signifikante oder das abhängig von der Plattform) ist der Wert m_num und die restlichen Bytes sind, was auch immer im Speicher nach m_num passiert. Abhängig von der Zeichencodierung der breiten Zeichen ist dies möglicherweise nicht einmal ein gültiges Zeichen. Auch wenn es gültig ist, ist es weitgehend Unsinn. (Es gibt andere mögliche Probleme, wenn wofstream::write eine wide-character-aligned anstelle einer byte-aligned-Eingabe erwartet, oder wenn m_num sofort von nicht lesbarem Speicher folgt).

  2. Wenn Sie wofstream verwenden, dann ist dies ein Chaos, und ich werde es nicht ansprechen. Wenn Sie zu einem Byte-orientierten ofstream wechseln, haben Sie zwei Möglichkeiten. 1. Wenn Sie immer nur die Datei auf demselben System lesen, funktioniert file.write(&myint64value,sizeof(myint64value)). Die Reihenfolge, in der die Bytes des 64-Bit-Werts geschrieben werden, ist undefiniert, aber die gleiche Sequenz wird verwendet, wenn Sie zurücklesen, das ist also egal. Do not versuchen Sie etwas analoges mit wofstream, weil es gefährlich ist! 2. Extrahiere jedes der 8 Bytes von myint64value separat (verschiebe um ein Vielfaches von 8 Bits nach rechts und nimm dann die unteren 8 Bits) und schreibe es dann. Dies ist vollständig portierbar, da Sie die Reihenfolge steuern, in der die Bytes geschrieben werden.

+0

Danke @nugae! Über Punkt # 2 ist das Problem Endianess, ist das richtig? Funktionen wie 'htons',' htonl' und Freunde (im Grunde den Standard auf Big-Endian setzen) würde das Problem lösen, liege ich falsch? – Asaf

+0

Ja, es ist Endianness. Solange Sie innerhalb eines Systems bleiben, spielt es keine Rolle, aber wenn Sie Kompatibilität zwischen Systemen wünschen, dann tut es das auch. 'htonl' und seine Verwandten würden funktionieren, aber (laut der Dokumentation) gehen sie nur bis zu 'uint32_t'. Wenn du also "uint64_t" machen willst, musst du die untere Hälfte ('& 0xffffffffU') und die obere Hälfte (' >>32') separat machen. Sie könnten das in Ihre eigene 'htonl64'-Funktion oder (besser) in Ihre eigenen' write64'- und 'read64'-Funktionen packen. – nugae

Verwandte Themen