Lesen binärer Dateien in array
Hallo
Ich bin gerade dabei ein kleines Programm zu schreiben. Dabei muss (für ein 3D-Spiel) ein Array vom Typ folgender Struktur aus einer Datei gelesen werden:
Nun habe ich das einlesen mit verschiedenen Varianten getestet:
fread - liest leider keinen Text ein, obwohl keine Fehler angezeigt werden.
ifstream::get - liest leider nur zeichen ein, aber wandeld diese nicht in ein Float-Wert um
Allerdings, wie man lesen kann , scheint keine Funktion richtig das zu tun, was ich möchte, denn keine Dieser Funktionen liefert einen Wert zurück.
Hat jemand eine Lösung für dieses Problem?
Danke im vorraus.
Ich bin gerade dabei ein kleines Programm zu schreiben. Dabei muss (für ein 3D-Spiel) ein Array vom Typ folgender Struktur aus einer Datei gelesen werden:
struct Vertex3d
{
FLOAT x, y, z;
DWORD color;
};
Nun habe ich das einlesen mit verschiedenen Varianten getestet:
fread - liest leider keinen Text ein, obwohl keine Fehler angezeigt werden.
ifstream::get - liest leider nur zeichen ein, aber wandeld diese nicht in ein Float-Wert um
Allerdings, wie man lesen kann , scheint keine Funktion richtig das zu tun, was ich möchte, denn keine Dieser Funktionen liefert einen Wert zurück.
Hat jemand eine Lösung für dieses Problem?
Danke im vorraus.
Bitte markiere auch die Kommentare, die zur Lösung des Beitrags beigetragen haben
Content-ID: 118004
Url: https://administrator.de/forum/lesen-binaerer-dateien-in-array-118004.html
Ausgedruckt am: 24.12.2024 um 16:12 Uhr
7 Kommentare
Neuester Kommentar
Hallo,
fehlt dir vielleicht ein reinterpret_cast?
Mein letztes c++ ist schon wieder ein weilchen her. Aber Serialisierung von komplexen Datenstrukturen geht mit einem ostream (öffnen mit ios::binary|ios::trunc empfehlenswert) ziemlich gut.
Schreiben dann mit
s.write(reinterpret_cast<char *>(&x),sizeof(x));
und einlesen mit
s.read(reinterpret_cast<char *>(&x), sizeof(x));
Gruß
Filipp
fehlt dir vielleicht ein reinterpret_cast?
Mein letztes c++ ist schon wieder ein weilchen her. Aber Serialisierung von komplexen Datenstrukturen geht mit einem ostream (öffnen mit ios::binary|ios::trunc empfehlenswert) ziemlich gut.
Schreiben dann mit
s.write(reinterpret_cast<char *>(&x),sizeof(x));
und einlesen mit
s.read(reinterpret_cast<char *>(&x), sizeof(x));
Gruß
Filipp
Hallo,
wenn du nicht postest, wie du deinen stream deklarierst kann man auch schlecht sagen, wo da der Fehler sein könnte.
Aber ich habe einen Fehler bei meinem Post entdeckt: ich verwende oFstream, nicht ostream.
Habe mal ein altes Beispiel ausgekramt.
Wobei in einer entsprechenden Header-Datei
Wieder einlesen kann man das ganze dann mit
Gruß
Filipp
wenn du nicht postest, wie du deinen stream deklarierst kann man auch schlecht sagen, wo da der Fehler sein könnte.
Aber ich habe einen Fehler bei meinem Post entdeckt: ich verwende oFstream, nicht ostream.
Habe mal ein altes Beispiel ausgekramt.
void FilterMain::serialize(SimObjekte& so, char* dateiname){
//Datei zur Serilaisierung öffen/anlegen
SerializeStream ostream(dateiname, ios::binary|ios::trunc);
if(!ostream)
throw SchUSi::FileNotFound(string("Die Datei ")+dateiname+" konnte nicht zum Speichern geoeffnet werden.");
/** Config::Dateiformatversion: Bei jeder Änderung am Dateiformat sollte diese
Konstante auf einen anderen Wert gesetzt werden, Dateien im alten Format
(was beim Einlesen Fehler erzeugen würde) können dann nicht mehr
geöffnet werden */
ostream.write(reinterpret_cast<char *>(&(Config::Dateiformatversion)),sizeof(Config::Dateiformatversion));
SimObjekt::Realtyp rt;
for (unsigned int i = 0; i < so.size(); i++) { //iterieren über alle SimObjekte
rt = (*so[i]).getRealtyp();
ostream.write(reinterpret_cast<char *>(&rt),sizeof(rt)); //erst speichern, um was für ein Objekt es sich gerade handelt (Realtyp)
(*so[i]).serialize(ostream); //dann Objekt selber
}
ostream.close(); //Serilisierung fertig, Datei schliessen.
}
typedef std::ofstream SerializeStream;
typedef std::ifstream DeserializeStream;
vorgenommen ist. SimObject: abstrakte Basisklasse der zu serialisierenden Klassen. Realtyp: SimObjekt hat mehrere abgeleitete Klassen. Beim Deserialisieren muss man aber wissen, von welcher konkreten das kommende Objekt ist. Dazu bekommt einfach jedes konkrete Basisklasse eine Int-Konstante zugewiesen, die steht vor den eigentlichen Objektdaten im Stream und zeigt somit, wie die kommenden Daten zu behandeln sind. Dateiformat ist ein int.typedef std::ifstream DeserializeStream;
Wieder einlesen kann man das ganze dann mit
void FilterMain::deserialize(SimObjekte& so, const char* dateiname){
//Datei zu Deserialisierung öffnen
DeserializeStream istream(dateiname, ios::binary|ios::in);
if(!istream)
throw SchUSi::FileNotFound(string("Die Datei ")+dateiname+" konnte nicht zur Einlesen geoeffnet werden.");
SimObjekt* simO;
/** Config::Dateiformatversion: Bei jeder Änderung am Dateiformat sollte diese
Konstante auf einen anderen Wert gesetzt werden, Dateien im alten Format
(was beim Einlesen Fehler erzeugen würde) können dann nicht mehr
geöffnet werden */
int DateiformatVersion;
istream.read(reinterpret_cast<char *>(&DateiformatVersion), sizeof(DateiformatVersion));
if (DateiformatVersion != Config::Dateiformatversion) {
throw SchUSi::StdError("Die verwendete Datei besitzt das falsche Format, bitte erneut erzeugen.");
}
while (istream) {
// hier wurde dann bis zum Ende des Streams gelesen. Dabei kommt immer erst der oben geschrieben RealTyp,
// passend zu diesem wird dann ein leeres Objekt dieses Typs erzeugt, dessen Deserialisierungsmethode
// aufgerufen (istream übergeben). Das Objekt liest dann seine Attributwerte aus dem Stream.
}
Gruß
Filipp
Zu reinterpret_cast nochmal: Der ist genau genommen teufelszeug weil er die Verwandlung von Äpfeln in Bananen zulässt ohne auch nur zu wissen, dass beides Obst ist (will heißen: es wird nicht wirklich empfohlen ihn zu verwenden, wo es sich vermeiden lässt).
ofstream::write erwartet einen char* und eine Schreiblänge. Dateiformatversion ist ein int. Und mit "reinterpret_cast<char *>(&(Config::Dateiformatversion))" wird ein char* erzeugt, der auf diesen int zeigt. Damit ist ofstream glücklich und klatscht die Daten in den Stream/File (und mit sizeof(Config::Dateiformatversion) gibt man an, wie weit das gehen soll).
Beim Einlesen ist es das gleiche:
ifstream::read ( char* s, streamsize n ); liest n Byte aus dem Stream und schreibt sie an die Stelle, auf die s zeigt.
Da ich weiß, dass im Stream als nächstes "DateiformatVersion" folgt will ich also sizeof(DateiformatVersion) einlesen. Schreiben will ich das eigentlich an DateiformatVersion*, was die read-Methode aber nicht schlucken würde. Also mache ich vorher ein char* draus.
Gruß
Filipp
Aber was mich schon jetz fragwürdig macht ist, wie ich die eingelesene Zeichenkette(char *) dann in ein float wert, geschweigendenn in einen DWORD wert umwandeln soll.
Das ist schon gesehen.ostream.write(reinterpret_cast<char *>(&(Config::Dateiformatversion)),sizeof(Config::Dateiformatversion));
Beim Einlesen ist es das gleiche:
ifstream::read ( char* s, streamsize n ); liest n Byte aus dem Stream und schreibt sie an die Stelle, auf die s zeigt.
Da ich weiß, dass im Stream als nächstes "DateiformatVersion" folgt will ich also sizeof(DateiformatVersion) einlesen. Schreiben will ich das eigentlich an DateiformatVersion*, was die read-Methode aber nicht schlucken würde. Also mache ich vorher ein char* draus.
Gruß
Filipp
Hat jemand eine Lösung für das einlesen von binären
Zeichen und die umwandlung in einen Float wert?
Ich dachte, die hätte ich dir das gerade realtiv ausführlich dargelegt. Und im Internet findet man dazu auch noch einiges unter dem Begriff "c++ serialization".Zeichen und die umwandlung in einen Float wert?
Und wenn du das alles von mir nicht hören willst lass dir wenigstens sagen: ifstream::get ist für deinen Zweck ungeeignet, weil es nur ints zurückgeben kann. Das kannst du der Definition der Methode entnehmen und es steht in jeder C++-Referenz. Verwende stattdessen ifstream::read, das Daten blockweise binär einlesen kann.
Gruß
Filipp