Vector in File schreiben und auf bestimmte Byte Größe aufblasen



  • Ich habe einen Vektor den ich mit fwrite in ein File schreibe. Das sieht grob so aus:

    // Build File Header 
    vector <string> FileHeader (15);
    FileHeader.at(0) = "blabla";
    // at(1) usw.
    
    double VectorByteSize = 0;
    
    // Write vector to file
    for(vector<string>::iterator 
    	i = FileHeader.begin();
    	i != FileHeader.end(); i++)
    {
           fwrite(&(*i),sizeof(string),1,MyFile);
           VectorByteSize += sizeof(string);
    }
    cout << "Vector Size in Bytes: " << VectorByteSize << "\n";
    

    Der Vektor der in der Datei landet hat dann eine bestimmte Größe z. B. 420 Bytes. Das ganze landet in einem RAW File, das man später in einem anderen Programm wieder auseinanderpflücken muss, sprich die Header von den Bildern trennen. Damit das einfacher geht auch wenn man den Vektor später mal vergrößert hätte ich gerne eine fixe Headergröße von z. B. 4096 Byte.

    Ich habe mir schon ein bisschen überlegt wie das funktionieren könnte, bin allerdings noch zu keiner wirklichen Lösung gekommen. Den "FileHeader" Vektor auf 4096 Byte "aufzublasen" halte ich für keine sonderlich gute Idee und wüsste auch gar nicht wie das geht. Alternativ könnte man evtl. einen zweiten Vektor oder ein Array anlegen das die Größe 4096-Vektorgröße hat. Oder ich schreibe viele Newlines in mein File bis der File Pointer an einer gewissen Position angekommen ist.

    Was haltet ihr für am besten zu programmieren und für am performantesten? Oder gibt es einen viel einfacheren Weg?



  • Was spricht gegen 0-Bytes?



  • asdrubael schrieb:

    ...

    // Build File Header 
    vector <string> FileHeader (15);
    FileHeader.at(0) = "blabla";
    // at(1) usw.
    
    double VectorByteSize = 0;
    
    // Write vector to file
    for(std::vector<string>::iterator 
    	i = FileHeader.begin();
    	i != FileHeader.end(); i++)
    {
           fwrite(&(*i),sizeof(string),1,MyFile);
           VectorByteSize += sizeof(string);
    }
    cout << "Vector Size in Bytes: " << VectorByteSize << "\n";
    

    ...

    Falls Du std::string verwendest:
    Kann man machen ... tut aber vermutlich nicht, was Du willst 😉
    Im string-Objekt selbst liegen nur die "Verwaltungsdaten", nicht jedoch der "Inhalt". Der liegt "auf dem Heap" (also im Speicher).
    Schreibst Du nun diese Daten in ein File, wird dort nur die "Adresse auf dem Heap" abgelegt ... mit der Du nicht viel anfangen kannst, wenn Du das Programm eiunmal beendet hast - weil dann da nichts mehr liegt (was Dich interessieren dürfte).

    Machs doch einfacher:

    // Build File Header 
    vector <string> FileHeader;
    FileHeader.push_back("blabla");
    FileHeader.push_back("2. blabla");
    ...
    
    ofstream out("Filename");
    for(std::vector<string>::const_iterator // da Du nicht in den string schreiben willst: const_iterator besser
    	i = FileHeader.begin();
    	i != FileHeader.end(); ++i)
    {
           out << *i << "\n";
           VectorByteSize += i->size();
    }
    

    @Fachlichkeit: Warum willst Du den Fileheader künstlich "statisch machen" ?
    Was spricht dagegen, ihn dynamisch zu machen ?
    Simpel:

    ofstream out("Filename");
    out << FileHeader.size() << "\n"; // erstmal die Zeilenzahl
    for(std::vector<string>::const_iterator i = FileHeader.begin();	i != FileHeader.end(); ++i) {
           out << *i << "\n";
           VectorByteSize += i->size();
    }
    // Hier kannst Du nun die Daten reinschreiben, die sonst noch so ins File gehören.
    

    Beim Einlesen dann einfach andersherum:

    ifstream in("Filename");
    size_t lineCount;
    in >> lineCount;
    
    string line;
    for(size_t i = 0; i <lineCount; ++i) {
       getline(in, line);
       FileHeader.push_back(line);
    }
    

    (Fehlerbehandlung und "Tricks mit back_inserter&Co" habe ich mal ausgelassen)

    Gruß,

    Simon2.



  • Simon2 schrieb:

    asdrubael schrieb:

    ...

    // Build File Header 
    vector <string> FileHeader (15);
    FileHeader.at(0) = "blabla";
    // at(1) usw.
    
    double VectorByteSize = 0;
    
    // Write vector to file
    for(std::vector<string>::iterator 
    	i = FileHeader.begin();
    	i != FileHeader.end(); i++)
    {
           fwrite(&(*i),sizeof(string),1,MyFile);
           VectorByteSize += sizeof(string);
    }
    cout << "Vector Size in Bytes: " << VectorByteSize << "\n";
    

    ...

    Falls Du std::string verwendest:
    Kann man machen ... tut aber vermutlich nicht, was Du willst 😉
    Im string-Objekt selbst liegen nur die "Verwaltungsdaten", nicht jedoch der "Inhalt". Der liegt "auf dem Heap" (also im Speicher).
    Schreibst Du nun diese Daten in ein File, wird dort nur die "Adresse auf dem Heap" abgelegt ... mit der Du nicht viel anfangen kannst, wenn Du das Programm eiunmal beendet hast - weil dann da nichts mehr liegt (was Dich interessieren dürfte).

    Die Daten bleiben bei mir aber auch nach Beenden des Programms und Rechnerneustart korrekt im File. Ist das nun Zufall oder "Glück"? Leider finden sich kaum Beispiele wie man einen Vektor mit fwrite wegschreibt, deswegen habe ich aus einem anderen Forum diesen Codeteil übernommen. C++ Streams haben sich bei mir leider als zu langsam herausgestellt. Ich habe kurz folgendes zusammen gecodet:

    #include "windows.h"
    #include <fstream>
    using namespace std;
    
    const int NUM_RUNS = 100;
    
    int _tmain(int argc, _TCHAR* argv[])
    {
    	ofstream file;
    	file.open("Test.dat", ios::binary);
    
    	int arraySize = (1360*1024)*2;
    	char* array = new char[arraySize];
    	// Set array entries to zero
    	memset(array, 0, sizeof (char) * arraySize);
    
    	LARGE_INTEGER before;
        LARGE_INTEGER after;
    
    	QueryPerformanceCounter (&before);
    	for(int i=0; i<NUM_RUNS; i++)
    		file.write(array, arraySize);
    	file.close();
    	QueryPerformanceCounter (&after);
    
    	LARGE_INTEGER freq;
        QueryPerformanceFrequency (&freq);
        LONGLONG time = ((after.QuadPart - before.QuadPart) * 1000) / freq.QuadPart;
    	cout << "C++ Streams haben gebraucht: " << time << " ms \n";
    
    	FILE *cFile = NULL;
    	cFile = fopen ("Test2.dat", "wb+");
    
    	QueryPerformanceCounter (&before);
    	for(int i=0; i<NUM_RUNS; i++)
    		fwrite(array, arraySize, 1, cFile);
    	fclose(cFile);
    	QueryPerformanceCounter (&after);
    
        QueryPerformanceFrequency (&freq);
        time = ((after.QuadPart - before.QuadPart) * 1000) / freq.QuadPart;
    	cout << "C hat gebraucht: " << time << " ms \n";
    
    	getchar();
    
    	return 0;
    }
    

    Auf meinem PC ergibt das folgendes:
    C++ Streams haben gebraucht: 24532 ms
    C hat gebraucht: 8177 ms

    Das ist ein Problem, weil ich für ein Array nur ca. 100 Millisekunden Zeit habe. Wenn es hier noch schlaue Tweaks gibt immer her damit.

    @Fachlichkeit: Warum willst Du den Fileheader künstlich "statisch machen" ?
    Was spricht dagegen, ihn dynamisch zu machen ?

    Es spricht eigentlich nichts dagegen momentan ist es ja dynamisch. Nur habe ich später wie gesagt ein RAW File mit einem gewissen Header und dem Rest Bildinformation. Wenn ich den Vektor jetzt erweitere ändert sich die Größe des Headers und ich muss das entsprechend auslesen und in meinem anderen Programm anpassen. Wenn ich mir das selbst programmiere kann ich natürlich sagen alles bis zu einem gewissen Zeichen (; etc.) ist Header und der Rest Bildinformation aber in IrfanView, Matlab etc. geht das nicht. Zudem weiß ich z. B. nicht ob meine Strings wirklich immer eine gewisse Länge haben oder sich mal zur Laufzeit vergrößern. Das wird zu einem Problem wenn ich "RAW Sequenzen" speichern will, also mehrere RAW Files hintereinander.



  • asdrubael_ schrieb:

    ...
    Die Daten bleiben bei mir aber auch nach Beenden des Programms und Rechnerneustart korrekt im File. ...

    Das kann ich kaum glauben. Welchen Wert liefert den "sizeof(string)" bei Dir ? Und wie lang sind die Strings, die Du verwendest ? Vielleicht hast Du ja glück, dass Deine spezielle std::string-Implementation kleine Strings auf dem Stack ablegt, aber ich würde mich keinesfalls daarauf verlassen.
    Schonmal versucht, einen String zu schreiben, der länger ist, als "sizeof(string)" ? (Merke: sizeof wird zur Compilezeit aufgelöst - kann also nichts wissen über dynamische Längen).

    Zum Thema "Laufzeit bei streams" gab es hier irgendwo schonmal gründliche Untersuchungen und da kam eigentlich raus, dass sie nich prinzipiell langsamer sind - ich vermute halt, dass Du tatsächlich mit den streams die echten Daten (und damit viel mehr als mit der fwrite-Variante) wegschreibst ... was natürlich länger dauert als nur die Verwaltungsdaten.

    Gruß,

    Simon2.



  • Simon2 schrieb:

    asdrubael_ schrieb:

    ...
    Die Daten bleiben bei mir aber auch nach Beenden des Programms und Rechnerneustart korrekt im File. ...

    Das kann ich kaum glauben. Welchen Wert liefert den "sizeof(string)" bei Dir ? Und wie lang sind die Strings, die Du verwendest ? Vielleicht hast Du ja glück, dass Deine spezielle std::string-Implementation kleine Strings auf dem Stack ablegt, aber ich würde mich keinesfalls daarauf verlassen.
    Schonmal versucht, einen String zu schreiben, der länger ist, als "sizeof(string)" ? (Merke: sizeof wird zur Compilezeit aufgelöst - kann also nichts wissen über dynamische Längen).

    Ja es scheint fast so. Hab folgendes ausprobiert:

    MyFile = fopen (SequenceName, "wt+");
    vector <string> FileHeader;
    FileHeader.push_back("EinkurzerString");
    FileHeader.push_back("Ein ganz schön langer String. ABCDEFGHIJKLMNOPQRSTUVWXYZ");
    
    double VectorByteSize = 0;
    
    // Write vector to file
    for(std::vector<string>::const_iterator  
    	i = FileHeader.begin();
    	i != FileHeader.end(); i++)
    {
    	//cout << *i << endl;
    	fwrite(&(*i),sizeof(string),1,MyFile);
    	//VectorByteSize += i->size();
    	cout << sizeof(string) << endl;
    	cout << i->size() << endl;
    }
    //cout << "Vector Size in Bytes: " << VectorByteSize << "\n";
    fclose(MyFile);
    

    Ausgabe:
    28
    15
    28
    56

    Im File landet dann lediglich folgendes:
    ÍÍÍÍEinkurzerString

    IDE ist übrigens VS.Net 2003. Danke schon mal für den Hinweis, nach dem Fehler hätte ich nachträglich sicher lange gesucht.

    Zum Thema "Laufzeit bei streams" gab es hier irgendwo schonmal gründliche Untersuchungen und da kam eigentlich raus, dass sie nich prinzipiell langsamer sind - ich vermute halt, dass Du tatsächlich mit den streams die echten Daten (und damit viel mehr als mit der fwrite-Variante) wegschreibst ... was natürlich länger dauert als nur die Verwaltungsdaten.

    Das kann ich mir wiederrum schwer vorstellen, da ich in meinem Benchmark ja ein Array schreibe und die entstandenen Files (Test und Test2.dat) die gleiche Größe haben und binärkompatibel sind. Und ein Array muss ich schreiben (Bildinformationen) außer ich will den Header mit ofstream schreiben und das Array mit fwrite, da gibt es aber denk ich Probleme. Ich werde aber mal suchen und hoffe ich kann das ganze über C++ Streams lösen ohne das es länger dauert.



  • Hi,

    noch eine kurze Idee zum "Schreiben mit fwrite()": Da fwrite() eigentlich auf "C-Strings" ausgelegt, ist, solltest Du vielleicht auch diese schreiben:

    ...
    // Write vector to file
    for(std::vector<string>::const_iterator  
    	i = FileHeader.begin();
    	i != FileHeader.end(); i++)
    {
    	fwrite(i->c_str(),i->size(),1,MyFile);
    }
    ...
    

    Falls es sich um Binärdaten handelt (also '\0' oder andere nonprintables auch vorkommen können), kannst Du statt c_str() auch data() nehmen und die Länge voranstellen.

    Gruß,

    Simon2.



  • Simon2 schrieb:

    Hi,

    noch eine kurze Idee zum "Schreiben mit fwrite()": Da fwrite() eigentlich auf "C-Strings" ausgelegt, ist, solltest Du vielleicht auch diese schreiben:

    ...
    // Write vector to file
    for(std::vector<string>::const_iterator  
    	i = FileHeader.begin();
    	i != FileHeader.end(); i++)
    {
    	fwrite(i->c_str(),i->size(),1,MyFile);
    }
    ...
    

    Danke, das schreibt den Vektor korrekt weg auch wenn ich lange Strings verwende. Wegen dem fixen Fileheader probier ich jetzt mal dynamisch ein Array mit Newlines anzulegen und ergänze dann hier noch.



  • asdrubael schrieb:

    ...
    Danke, das schreibt den Vektor korrekt weg auch wenn ich lange Strings verwende...

    Jupp !
    c_str() ist genau dafür gedacht, C-String-Schnittstellen zu bedienen. string stellt dann die Daten so zur Verfügung, dass das passt (wie es das macht, bleibt der Implementierung überlassen - es kann einfach eine Zeiger auf den interenn Speicher zurückgeben, kann es aber auch ganz anders machen).
    Aber NUR FÜR DAS LESEN !! (auch wenn das "c_" wohl nicht für "const" steht 😉 )

    Wenn auch geschrieben werden soll, musst Du ein eigenes char-Array anlegen und dann rüberkopieren.

    Gruß,

    Simon2.


Anmelden zum Antworten