Binärdatei bearbeiten



  • hmm,

    eigentlich sollte der Inhalt ungefähr so aussehen:

    - die erste Zahl ist eine Integer und
    - dann durch leerzeichen getrennte float-Zahlen



  • und die bits werden eben als Zeichen interpretiert => da kommt Chaos raus.



  • MarcR schrieb:

    hmm,

    eigentlich sollte der Inhalt ungefähr so aussehen:

    - die erste Zahl ist eine Integer und
    - dann durch leerzeichen getrennte float-Zahlen

    Das ist aber nicht wirklich binär 😉 (unter "Binärdatei" verstehen die meisten hier, wenn du deine Daten in dem Format speicherst, in dem sie im Rechner verwaltet werden)



  • Ich war so frei und habe mal die Datei um die es mir geht hochgeladen.

    http://home.arcor.de/reichert13/Test1.dat
    

    Die Datei Test1.dat:
    - enthält binär ausgegebene Zahlenwerte
    - der erste ist ein Integer-Wert und gibt die Anzahl der nachfolgenden Floatwerte an

    Mit dem Integer-Wert dimensioniere ich ein Float Array. Nun möchte ich die nachfolgenden Float-Zahlen in ein Float-Vektor lesen.

    FILE *pfile;
    
    	int size;
    
    	if ( fopen_s( &pfile, "Test1.dat", "rb" ) != 0 )
    
    		fprintf( stderr, "Trouble opening file\n" );
    
    	else
    
    	{
    
    	if ( !feof(pfile) ) fscanf_s(pfile, "%d", &size);
    	if ( !ferror(pfile) && size > 0 )
    			{
    
    		cout << size << endl;
    
    		float* pArr = new float[size];
    
    /* ---------- ab hier hapert es --------------------------------- 
    
        	for (unsigned int ix = 0; ix < size && !feof(pfile); ix++)
    		{
    			fscanf_s(pfile, "%d", &pArr[ix]);
    			cout << pArr[ix] << endl;
    		}
    
    -------------------------------------------------------------------- */
    	}
    	fclose( pfile );
    	}
    

    Also wenn ich die Datei so öffne ist der erste Integerwert bei mir = 4198489.
    Mit dem das Array dimensioniert wird. Mein Bauch verrät mir, der Wert könnte etwas zu groß sein. Oder?

    Wie komme ich dann sinnvolerweise an die floatwerte?



  • *wirft einen kurzen Blick in die Datei* ja, eindeutig Binärdaten. Da wird fscanf() wohl kaum etwas sinnvolles rauslesen können - sowas liest man mit fread() bzw. istream::read() ein.



  • 2 kurze fragen (nicht böse gemeint):

    Hast du dich mit dem Thema überhaupt näher befasst?

    Verstehst du auch, was du da programmierst oder "kopierst" du nur code?

    Ich werd später wenn ich Zeit hab das evtl. mal versuchen.



  • Der Gedanke mit fread kam mir auch. Das Problem bzw. die Eigenschaft von fread ist ein Blockweises auslesen. Allerdings finde ich dort auf anhieb keinen passenden Ansatzpunkt.

    - wenn ich in festen Blöcken springe, laufe ich gefahr das ich 2 Float-Werte zusammen in einem Block habe

    ich schau mal wie weit ich mit fread komme.

    @XaTrIxX ich nehme es dir nicht böse. Deine Aussage bezieht sich sicherlich auf die for()-Schleife, auch zurecht. Aber dieses war nur ein Versuch.



  • Bei fread kannst du auch angeben, wie groß der einzulesende Block werden soll - und da gibt man natürlich die Größe an, die man tatsächlich erwartet (sagt dir sizeof() etwas?).



  • Als erstes öffnen wir einen Datei-Stream. Binär, so steht es ja nunmal in der Datei (Warum eigtl.?). Ob das nun geklappt hat, sollte man auch überprüfen.

    std::ifstream file_stream("Test1.dat", std::ios::binary);
    if (!file_stream) throw std::runtime_error("could not open file \"test1.dat\"");
    

    Das ist ja schonmal ein Anfang. Jetzt mal versuchen die Ganzzahl auszulesen (Was um Himmelswillen ist ein "Integer-Wert" in dieser Satzkonstruktion? "Integer" = "Ganzzahl")! Ein Array kann aber natürlich nur eine positive Anzahl an Elementen haben. In der Annahme, dass der Macher der Datei einen hinreichenden Kenntnisstand hatte, lesen wir also einen unsignierte (ohne +/-) Ganzzahl ein.
    Wie schon erwähnt, versuchen wir das ganze, von d.h. sollten wir auch gucken ob es geklappt hat!

    unsigned int arr_size(0);
    file_stream.read(reinterpret_cast<char*>(&arr_size), sizeof(unsigned int));
    if (!file_stream) throw std::runtime_error("\"test1.dat\" is corrupted");
    

    Jetzt sollen ganz viele Fließkommazahlen(float's) kommen. Also legen wir schonmal einen Array an oder nehmen einen Container-Typ des Standards, und reservieren genügend Speicher.

    float* arr_data(new float[arr_size]);
    

    oder

    std::vector<float> arr_data(arr_size);
    

    .

    Ein leerer Array hilft nur wenig, also kommen jetzt die Daten. Wenn nun aber irgendwas mit den Daten in der Datei nicht stimmt, kann man zum einen hingehen, und sagen, dass die Datei beschädigt ist, oder hingehen und einen Standardwert nehmen.

    for (unsigned int i(0); i < arr_size; ++i)
    {
        file_stream.read(reinterpret_cast<char*>(&arr_data[i]), sizeof(float));
        if (!file_stream) std::runtime_error("\"test1.dat\" is corrupted");
    }
    
    for (unsigned int i(0); i < arr_size; ++i)
    {
        file_stream.read(reinterpret_cast<char*>(&arr_data[i]), sizeof(float));
        if (!file_stream) { file_stream.clear(); arr_data[i] = 0.0f; }
    }
    

    .

    Jetzt haben wir alle Daten und sind glücklich!
    Wenn wir uns am Anfang nicht für den Standardcontainer entschieden haben, sondern lieber selbst per new[] einen Array erzeugt haben, müssen wir diesen am Ende nochmal freigeben.

    delete [] arr_data;
    

    .

    Und das war es auch schon 🙂 Hoffe, das du jetzt nicht nur kopiert (dann klappt der Code übrigens nicht!) sondern es auch verstehst!



  • hmm, ich hab es etwas erweitert. Dabei habe ich einen neuen Integer-Wert am Anfang ausgelesen der, wie ich finde passender sein könnte.

    Wenn ich die *.dat in einen Hexeditor lade, ist der erste Wert die 13. Daraus folgt das wenn man den Hex-Wert umrechnet die erste zahl eine Dezimal 19 sein müßte.

    Könnte das stimmen?

    Demzufolge hätte erstmal mein Float-Array eine größe von 19.

    FILE *pfile;
    	fpos_t pos;
    
    	int size[1];
    	float buffer[40];
    
    	if ( fopen_s( &pfile, "Test1.dat", "rb" ) != 0 )
    
    		fprintf( stderr, "Trouble opening file\n" );
    
    	else
    
    	{
    
    			fread(size, sizeof( int ), 2, pfile );
    			if ( fgetpos( pfile, &pos ) != 0 )
    				printf( "fgetpos error" );
    
    		float* pArr = new float[static_cast<int>(size[0])];
    			cout << "\n\n-->> " << size[0] << endl;
    
    			if ( fsetpos( pfile, &pos ) != 0 )
    				printf( "fsetpos error" );
    				fread( buffer, sizeof( float ), 19, pfile );
    
    				fclose( pfile );
    
                     for(int i = 0; i < 19; i++)
                         cout << " " << pArr[i];
    
    	}
    

    Hmm, so sieht es momentan bei mir aus. Bin mir nicht sicher ob ich richtig bin.



  • MarcR schrieb:

    Wenn ich die *.dat in einen Hexeditor lade, ist der erste Wert die 13. Daraus folgt das wenn man den Hex-Wert umrechnet die erste zahl eine Dezimal 19 sein müßte.

    Könnte das stimmen?

    Ein int hat typischerweise die Größe von 4 Byte - d.h. du müsstest die ersten 4 Byte zusammenfassen, um die Arraygröße vergleichen zu können.

    PS: Warum verwendest du nicht den Lösungsansatz, den (D)Evil präsentiert hat?



  • Interessant. Ich sass noch an dem Beitrag als Devil schon gepostet hat, deswegen konnte ich mir das noch nicht ansehen.



  • Ich bedanke mich recht herzlich.

    Dank Devils Vorschlag habe ich einen guten Vergleich der Endresultate.

    Mir ist aufgefallen das bei dem Vorschlag den ich kurz vor Devil gepostet habe die erste Float fehlt(die erste Float nach dem Integer ). Diese wird mir nicht ausgegeben? während sie bei der Variante von Devil schön da steht.

    Woran kann das liegen? Springe ich mit dem zeiger vorbei?



  • Naja dein Code ist C ... solltest du unter C++ eh meiden, aber nun zu deinem Fehler. Guck dir evtl. mal http://www.cppreference.com/stdio/fread.html an. du ließt ein:

    fread(size, sizeof( int ), 2, pfile );
    

    Naja, sowas:

    int size[1];
    

    ist nicht soo die feine Art.

    unsigned int size;
    // ...
    fread(&size, sizeof(unsigned int), 1, pFile);
    

    Dann

    float* pArr = new float[static_cast<int>(size[0])];
    

    . Hier ist der static_cast überflüssig, da du eh einen int zurück gebkommst. size[0] => int. Ein *size tut es in dem Fall aber auch. Wenn du aber direkt einfach size als unsigned int (NICHT als Array) anlegst, hast du da einfach size stehen.


Anmelden zum Antworten