Binärdateidefiniton mit Arrays -> einlesen in std::vector ?



  • Okay, angenommen ich habe eine Definition einer Binärdatei, die so aussieht:

    // Dateiaufbau
    struct Vertices
    {
      int vertexAnzahl;
    
      Vertex vertices[vertexAnzahl];
    };
    
    // Das dazugehörige struct:
    struct Vertex
    {
      float x;
      float y;
      float z;
    };
    

    Ich habe dazu folgende Fragen:

    1. Ich kann zuerst die vertexAnzahl einlesen, und dann nach und nach die einzelnen vertices in einen std::vector pushen. Die folgende Lösung funktioniert, ist aber nicht sonderlich effektiv:
    std::vector<Vertex> vertices;
    for(int i=0;i<vertexAnzahl;++i)
    {
      Vertex tmpVertex;
      datei.read (reinterpret_cast<char*>(&tmpVertex),sizeof(Vertex));
      vertices.push_back(tmpVertex);
    }
    

    Kann ich das ohne Schleife machen, so daß ich den std::vector auf einmal fülle? Der Standard garantiert ja, daß Elemente eines vectors hintereinander gespeichert werden. Ich habe folgendes probiert, hat aber nicht funktioniert:

    std::vector<Vertex> vertices(vertexAnzahl);
    datei.read (reinterpret_cast<char*>(&vertices),sizeof(Vertex) * vertexAnzahl);
    
    1. Können bei meiner Lösung Probleme hinsichtlich der Endianness entstehen?
    2. Wie sähe es aus bei mehrdimensionalen Arrays, wäre der Dateikopf so:
    // Dateiaufbau mehrdimensionales Array
    struct Vertices
    {
      int vertexAnzahl_1;
      int vertexAnzahl_2;
    
      Vertex vertices[vertexAnzahl_1][vertexAnzahl_2];
    };
    

    Ich bin mir sicher daß es eine Möglichkeit gibt, alles auf einmal einzulesen (was natürlich schneller wäre).
    Bin für jede Hilfe dankbar 🙂



  • cubeman schrieb:

    ...

    // Dateiaufbau
    struct Vertices
    {
      int vertexAnzahl;
    
      Vertex vertices[vertexAnzahl];
    };
    
    // Das dazugehörige struct:
    struct Vertex
    {
      float x;
      float y;
      float z;
    };
    

    ...

    Merke: Das Binäre Speicherlayout dieser beiden structs ist nicht festgelegt...

    Gruß,

    Simon2.



  • Hm, das struct "Vertex" ist natürlich nicht mehr in der Datei gespeichert, das habe ich nur ergänzend geschrieben - war mißverständlich...
    Der Dateiaufbau ist also nur:

    // Dateiaufbau
    struct Vertices
    {
      int vertexAnzahl;
    
      Vertex vertices[vertexAnzahl];
    };
    

    Die Dateigröße ist somit

    sizeof(int) + vertexAnzahl * sizeof(Vertex)
    

    Das Speicherlayout müßte doch dann schon eindeutig sein, oder liege ich da falsch ?



  • cubeman schrieb:

    ...
    Das Speicherlayout müßte doch dann schon eindeutig sein, ...

    Nein.

    cubeman schrieb:

    ...
    ...liege ich da falsch ?

    Ja. 😉

    Stichwort: Padding und Alignment.

    am Besten versuchst Du nicht, das ganze auf einen Schlag einzulesen, sondern schön elementweise.

    Gruß,

    Simon2.



  • Danke erstmal für die hilfreichen Antworten!

    Die Frage ist nun, wie kann ich überhaupt sicherstellen, daß die Daten korrekt eingelesen werden?

    Ich weiß, daß...

    • ...die Datentypen der Datei float und int garantiert 32 bittig sind
    • ...die Endianness der Datei anscheinend gleich ist wie bei meinem x86 System - Little Endian (da meine gepostete Methode richtige Werte geliefert hat)

    Das heißt, auf einem Big Endian System würde schon das erste Element vertexAnzahl falsch eingelesen werden, da die Byte-Reihenfolge anders wäre?
    Dies müßte ich dann auch beim elementweisen Einlesen beachten.

    Alles auf einmal zu lesen wird's somit sowieso nicht spielen 😞

    Tja, Datenspeicherung in Textdateien hat schon sein Gutes...



  • cubeman schrieb:

    ...
    Tja, Datenspeicherung in Textdateien hat schon sein Gutes...

    Eben. 😉

    Am Besten liest Du die Datei byteweise ein und interpretierst es entsprechend "Element für element":

    // Annahme: Im File steht eine Zahl und dann ein 0-Terminierter String
    struct A {
       int i;
       string s;
    };
    
    int main() {
       A a;
    
       ifstream in("myfile.dat", ios_base::bin);
       char inBuf[2];
    
    // Annahme: Du weißt, dass das Integer als 2 Byte BigEndian abgespeichert ist
       in.read(inBuf, 2);
       if(in) { // nur, bei Erfolg
          a.i = inBuf[0]=0x100 + inBuf[1];
       }
    
       while(in.read(inBuf, 1) && inBuf != '\0') a.s += inBuf[0];
    ...
    

    Ist nur ein Minimalbeispiel ... stilistisch würde ich da noch Einiges anders machen und das Ganze evtl. in den operator>>(..., ifstream&) packen.

    Gruß,

    Simon2.


Log in to reply