 |
C++Talk.NET C++ language newsgroups
|
| View previous topic :: View next topic |
| Author |
Message |
Thomas Hühn Guest
|
Posted: Tue Mar 20, 2007 3:16 pm Post subject: Eigenen Typ in Datei schreiben |
|
|
Hallo
Ich habe einen eigenen Typ, der Einfachheit halber mal nur das hier:
struct Item {
unsigned int number;
double weight;
};
Nun möchte ich den über einen ofstream in eine Binärdatei schreiben und
später wieder auslesen.
Das würde ich jetzt mal so anfangen:
std::ostream& operator<< (std::ostream& out, const Item& i) {
// out << i.number << i.weight;
// bzw.
// out.write((char *)&i, sizeof(Item));
return out;
}
So, zwei Möglichkeiten. Per operator<< für die primitiven Typen oder per
write() "direkt".
Tatsächlich werde ich viele Items schreiben (einige hunderttausend) und
die Items sind potentiell nicht einfach eine struct mit zwei primitiven
Typen, sondern sind auch mal Klassen mit Elementfunktionen und
std::vector drin und sowas.
write dürfte schneller sein, da unformatierter Output, richtig? Außerdem
wäre ein simples Wegschreiben natürlich einfacher als die Elemente der
Klasse "sauber" zu serialisieren.
Ist mir umgekehrt beim Einlesen garantiert, daß ich daraus per
reinterpret_cast (ja, oben ist ein C-style-cast, der ist nur der
Schreibfaulheit halber hier im Beispiel drin) wieder ein Item-Objekt
formen kann? Ich hoffe doch. Oder können mich da böse Überraschungen
erwarten?
Portabilität der Binärdatei auf andere Architekturen, andere Maschinen
derselben Architektur, andere Kompilate der Quellen etc. ist dabei
natürlich nicht notwendig.
Die Teile, die ich auf anderen Kisten verwenden können will (und wo
Performace eh untergeordnet ist), serialisiere ich in Textdateien.
Habe ich da soweit einen groben Denkfehler drin?
Thomas |
|
| Back to top |
|
 |
Marcel Müller Guest
|
Posted: Wed Mar 21, 2007 12:55 am Post subject: Re: Eigenen Typ in Datei schreiben |
|
|
Thomas Hühn wrote:
| Quote: | Tatsächlich werde ich viele Items schreiben (einige hunderttausend) und
die Items sind potentiell nicht einfach eine struct mit zwei primitiven
Typen, sondern sind auch mal Klassen mit Elementfunktionen und
std::vector drin und sowas.
write dürfte schneller sein, da unformatierter Output, richtig? Außerdem
wäre ein simples Wegschreiben natürlich einfacher als die Elemente der
Klasse "sauber" zu serialisieren.
|
Bei einer binären Serialisierung von POD Typen ohne Pointer ist das
dasselbe, solange keine Vorkehrungen für Endianness oder verschiedene
Compiler (Packing) benötigt werden. Im Prinzip kann man sogar ganze
Arrays solcher einfachen Strukturen in einem Rutsch laden und speichern,
sofern sie als vector im Speicher liegen.
| Quote: | Ist mir umgekehrt beim Einlesen garantiert, daß ich daraus per
reinterpret_cast (ja, oben ist ein C-style-cast, der ist nur der
Schreibfaulheit halber hier im Beispiel drin) wieder ein Item-Objekt
formen kann? Ich hoffe doch. Oder können mich da böse Überraschungen
erwarten?
|
Genau die oben genannten Überraschungen. Sprich, wenn die Datentypen für
diese Art der Serialisierung geeignet sind /und/ die binäre
Repräsentation der enthaltenen primitiven Typen sowie das Speicherlayout
der Struktur beim schreibenden und beim lesenden Programm identisch
sind, dann geht das, sonst nicht.
| Quote: | Portabilität der Binärdatei auf andere Architekturen, andere Maschinen
derselben Architektur, andere Kompilate der Quellen etc. ist dabei
natürlich nicht notwendig.
|
Dann ist es hinreichend, dass die Strukturen PODs sind und keine Pointer
enthalten.
| Quote: | Die Teile, die ich auf anderen Kisten verwenden können will (und wo
Performace eh untergeordnet ist), serialisiere ich in Textdateien.
Habe ich da soweit einen groben Denkfehler drin?
|
Nein. Aber wie gesagt, das ist eine Art der Serialisierung. Ganz ohne
Overhead kommt man auch in binären, nicht plattformübergreifenden
Serialisierungen nicht aus. Wenn zum Beispiel verschiedene Objekttypen
in eine Datei sollen. Dann muss man sich irgendwo merken, wieviele es
von welcher Sorte sind, damit man die Datei beim Einlesen wieder korrekt
auseinandernehmen kann.
Marcel |
|
| Back to top |
|
 |
Thorsten Nitz Guest
|
Posted: Wed Mar 21, 2007 4:06 pm Post subject: Re: Eigenen Typ in Datei schreiben |
|
|
Thomas Hühn schrieb:
| Quote: | Ich habe einen eigenen Typ, der Einfachheit halber mal nur das hier:
struct Item {
unsigned int number;
double weight;
};
Nun möchte ich den über einen ofstream in eine Binärdatei schreiben und
später wieder auslesen.
Das würde ich jetzt mal so anfangen:
std::ostream& operator<< (std::ostream& out, const Item& i) {
// out << i.number << i.weight;
// bzw.
// out.write((char *)&i, sizeof(Item));
return out;
}
Tatsächlich werde ich viele Items schreiben (einige hunderttausend) und
die Items sind potentiell nicht einfach eine struct mit zwei primitiven
Typen, sondern sind auch mal Klassen mit Elementfunktionen und
std::vector drin und sowas.
|
Das ist ein wesentlicher Unterschied: Dien oben angeführtes Item hat die
Eigenschaft, POD ("Plain Old Data") zu sein.
struct Item {
std::vector< int > numbers;
int* pi;
};
hat diese angenehme Eigenschaft nicht.
| Quote: | write dürfte schneller sein, da unformatierter Output, richtig?
|
Richtig.
| Quote: | Außerdem
wäre ein simples Wegschreiben natürlich einfacher als die Elemente der
Klasse "sauber" zu serialisieren.
|
Hier stellt sich die Frage, ob Du mehr Wert auf ein schnelles Programm
oder aber auf ein korrektes Programm legst. Letzteres erfordet eine
"saubere" Serialisierung. Mit write musst Du die halt selbst programmieren.
| Quote: |
Ist mir umgekehrt beim Einlesen garantiert, daß ich daraus per
reinterpret_cast (ja, oben ist ein C-style-cast, der ist nur der
Schreibfaulheit halber hier im Beispiel drin) wieder ein Item-Objekt
formen kann? Ich hoffe doch. Oder können mich da böse Überraschungen
erwarten?
|
Für Dein Item ja, für mein Item nein. Wenn Du spaßeshalber mal sizeof
vector<int> auf Deinem System ausgeben lässt, wirst Du feststellen, dass
ein solches Vector-Object nur wenige Bytes groß ist. Es enthält nämlich
im Wesentlichen nur einen Zeiger auf die eigentlichen Daten, keinesfalls
aber die Datensätze selber. Daraus folgt, dass write( numbers ) die
aktuelle Speicheradresse Deiner Daten in die Datei schriebt. Eine
Information, die Die beim Einlesen überhaupt nichts nützt. Du wirst im
operator<< also selber eine Schleife über alle Elemente programmieren
müssen.
Das gleiche Problem hast Du beim Element pi: mit write schreibst Du eine
Speicheradresse in die Datei, die beim Einlesen weder von deinem
Programm adressiert werden darf noch die gewünschte Information enthält.
Sollten Deine Items mit Vererbung, womöglich mehrfach, mit virtuellen
Methoden oder mit private, protected und public versehen sein, wird das
Speicherlayout der Objekte zusehends undurchsichtiger. Dem Compiler ist
es erlaubt, in die Struktur nicht nur Füllbytes, sondern auch
Verwaltungsinformationen einzubauen. Ob diese nach einem
Schreib-Lese-Zyklus noch sinnvoll interpretierbar sind, kann Dir
theoretisch höchstens der Compilerhersteller sagen. Wenn er bei Trost
ist, wird er diese Auskunft verweigern.
Vorstehendes gilt schon unter Beachtung der folgenden Voraussetzung:
| Quote: | Portabilität der Binärdatei auf andere Architekturen, andere Maschinen
derselben Architektur, andere Kompilate der Quellen etc. ist dabei
natürlich nicht notwendig.
|
Andererseits: Das sagen alle. Und stimmen tut es so gut wie nie. "Andere
Architektur" kann schon der selbe Compiler mit anderen Aufrufparametern
sein. Oder der gleiche Compiler in einer um 0.1 höheren Versionsnummer.
Statt des Wortes "natürlich" hätte ich "perverserweise" passender
gefunden;->
Tschö, wa!
Thorsten |
|
| Back to top |
|
 |
James Kanze Guest
|
Posted: Wed Mar 21, 2007 7:28 pm Post subject: Re: Eigenen Typ in Datei schreiben |
|
|
On Mar 20, 11:16 am, Thomas Hühn <newsgro...@thomas-huehn.de> wrote:
| Quote: | Ich habe einen eigenen Typ, der Einfachheit halber mal nur das hier:
struct Item {
unsigned int number;
double weight;
};
Nun möchte ich den über einen ofstream in eine Binärdatei schreiben und
später wieder auslesen.
|
In welchem Format (XDR, BER...)?
| Quote: | Das würde ich jetzt mal so anfangen:
std::ostream& operator<< (std::ostream& out, const Item& i) {
// out << i.number << i.weight;
|
Ich wurde mindestens ein Trennzeichen dazu fügen. Amsonstens
werden die Daten nicht wiederlesbar, weder von einem Mensch noch
durch ein Program.
| Quote: | // bzw.
// out.write((char *)&i, sizeof(Item));
|
Das << bedeutet formattiert. ostream bedeutet Text. Ich würde
*nicht* << von ostream hier benutzen.
| Quote: | return out;
}
So, zwei Möglichkeiten. Per operator<< für die primitiven Typen oder per
write() "direkt".
Tatsächlich werde ich viele Items schreiben (einige hunderttausend) und
die Items sind potentiell nicht einfach eine struct mit zwei primitiven
Typen, sondern sind auch mal Klassen mit Elementfunktionen und
std::vector drin und sowas.
|
Dann kannst du write vergessen.
| Quote: | write dürfte schneller sein, da unformatierter Output, richtig?
|
Besonders wenn die Objekte vector, string u.s.w. enthalten:-),
weil sie den Inhalt nicht ausschreibt. Also, wesentlich
schneller, aber... Ich schätze, du willst die Daten auch
irgendwann lesen, und dieselben Werte finden; dass kannst du mit
write nicht erreichen. (Mindestens nicht so einfach.)
| Quote: | Außerdem
wäre ein simples Wegschreiben natürlich einfacher als die Elemente der
Klasse "sauber" zu serialisieren.
Ist mir umgekehrt beim Einlesen garantiert, daß ich daraus per
reinterpret_cast (ja, oben ist ein C-style-cast, der ist nur der
Schreibfaulheit halber hier im Beispiel drin) wieder ein Item-Objekt
formen kann?
|
Wenn die struct dynamische Elemente enthält, wie std::vector
oder std::string, ist es sogar garantiert, dass du die
ursprunglichen Werte nicht wiederfindest; höchstwahrscheinlich
sogar bekommst du ein Core-Dump des lesenden Programms.
| Quote: | Ich hoffe doch. Oder können mich da böse Überraschungen
erwarten?
Portabilität der Binärdatei auf andere Architekturen, andere Maschinen
derselben Architektur, andere Kompilate der Quellen etc. ist dabei
natürlich nicht notwendig.
Die Teile, die ich auf anderen Kisten verwenden können will (und wo
Performace eh untergeordnet ist), serialisiere ich in Textdateien.
Habe ich da soweit einen groben Denkfehler drin?
|
Dass Daten nur aus einer Reihe Bytes bestehen.
Solange die Struct nur primitiven Daten enthält, also
ganzzählige und gleitkomma-Typen, enthält, kann es gehen,
insofern du immer denselben Compiler, dieselbe Version und
dieselben Optionen benutzt. Wobei das auch Probleme bei der
Entwicklung beursacht; das Debuggen ist viel leichter, wenn du
die Daten selber lesen kannst.
Ich frage mich auch, in wie fern der Unterschied bedeuten ist.
Es hängt sicher vom System (und der Bibliothek) an---den
Unterschied bei mir geht von Faktor 2 bis auf Faktor 10. Auf der
neusten Rechner, aber, auch Textformattiert dauert es unter eine
Sekunde um eine Millione Elemente zu schreiben. (Auf einem
uralten Sparc dagegen variert das formattierte Schreiben
zwischen 22 Sekunde und 5 Sekunde, je nach Compiler und
Bibliothek.)
--
James Kanze (GABI Software) email:james.kanze (AT) gmail (DOT) com
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34 |
|
| Back to top |
|
 |
|
|
You cannot post new topics in this forum You cannot reply to topics in this forum You cannot edit your posts in this forum You cannot delete your posts in this forum You cannot vote in polls in this forum
|
|