fread gibt mehr zurück als in der Datei drin ist.



  • Hallo zusammen,

    ich habe ein ganz einfaches Progrämmchen, mit dem ich eine Datei von der Platte auslese und im Konsolenfenster ausgebe:

    #include <iostream>
    
    using namespace std;
    
    int main(int argc, char *argv[])
    {
    
    	FILE * pFile;
    	long lngFileLen = 0;
    	size_t stBytesRead;
    	char * chrBuffer;
    
    // open file
    	cout << "INFO\tOpen file" << endl;
    	pFile = fopen("C:\\Files\\tmp\\FileHandling\\Test.doc", "r");
    	if (pFile == NULL)
    	{
    		// error: File not found
    		cout << "ERROR\tFile not found!" << endl;
    		return 1;
    	}
    
    // get file size
    	fseek(pFile, 0, SEEK_END);	//go to end
    	lngFileLen = ftell(pFile);
    	fseek(pFile, 0, SEEK_SET);	//go back to end
    
    // read file
    	cout << "INFO\tRead file" << endl;
    	chrBuffer = (char *)malloc(sizeof(char)*lngFileLen);	//malloc buffer
    	stBytesRead = fread(chrBuffer, lngFileLen, 1, pFile);	//read into buffer
    	if (stBytesRead != lngFileLen)
    	{
    		cout << "ERROR\tMore bytes read." << endl;
    	}
    
    // close file
    	cout << "INFO\tClose file" << endl;
    	fclose(pFile);
    
    // show file content
    	cout << "INFO\tFile content:" << endl;
    	cout << "--------------------------------------------------------------" << endl;
    	cout << chrBuffer << endl;
    	cout << "--------------------------------------------------------------" << endl;
    
    // terminate buffer
    	free (chrBuffer);
    
    // exit program
    	return 0;
    }
    

    Jetzt gibt er nach dem letzten Zeichen, das ich in die Datei geschrieben habe, immer noch ein paar weiter Zeichen aus.
    Wo ist denn der Fehler? Ich habe im Netz gesucht und alle Beispiele machen es auf die mehr oder weniger gleiche Art und Weise.

    Gruß
    Johnson



  • Ich kenn mich mit C-Filekrempel nicht so ganz aus - kanns sein dass er wenn er eof liest kein abschließendes '\0' in den Buffer schreibt? bzw. dass du ans ende deines Buffers keine Nullterminierung geschrieben hast?
    Schau dir mal die C++ filestreams und std::strings an. Was du da schreibst ist C vermischt mit C++ Ausgaben.



  • Die Vermutung von pumuckl ist richtig: fread schreibt kein Nullterminierungszeichen in den Puffer. Der <<-Operator für C-Strings erwartet aber ein solches, um die Stringlänge ermitteln zu können.

    Was Du machen könntest, dabei wird aber der Puffer einmal kopiert:

    cout << std::string( chrBuffer, lngFileLen );
    

    Das Problem hättest Du bei den binären Lesefunktionen der C++-Streams zwar auch, nichtsdestotrotz ist der Hinweis auf diese vollkommen richtig.

    EDIT: Aufruf des string-Konstruktors korrigiert.



  • Die C-Lösung wäre also, lngFileLen**+1** byte Speicher für den Buffer zu besorgen (manuell. *schauder*) und danach das Nullterminierungszeichen in buffer[lngFileLen] zu schreiben *zehennägelhochroll* 😉



  • pumuckl schrieb:

    Ich kenn mich mit C-Filekrempel nicht so ganz aus -

    bitte was?

    pumuckl schrieb:

    kanns sein dass er wenn er eof liest kein abschließendes '\0' in den Buffer schreibt? bzw. dass du ans ende deines Buffers keine Nullterminierung geschrieben hast?

    Kann nicht nur, ist sogar. 🙂

    @Johnson:
    guckst du hier:
    chrBuffer = (char *)malloc(sizeof(char)*lngFileLen +1); // oder calloc
    if ( chrBuffer == NULL )
    {
    // error handling or
    // hasta la visa!
    return 1;
    }
    chrBuffer[lngFileLen] = 0;
    // ready to go...
    stBytesRead = fread(chrBuffer, lngFileLen, 1, pFile);
    ...



  • CFile Kumpel schrieb:

    pumuckl schrieb:

    Ich kenn mich mit C-Filekrempel nicht so ganz aus -

    bitte was?

    Hab nie C gelernt, gleich mit C++ angefangen => C-FILEs erzeugen bei mir nur die "Das ist aber kein C++-Meckermecker"-Reaktion 😉



  • pumuckl schrieb:

    CFile Kumpel schrieb:

    pumuckl schrieb:

    Ich kenn mich mit C-Filekrempel nicht so ganz aus -

    bitte was?

    Hab nie C gelernt, gleich mit C++ angefangen => C-FILEs erzeugen bei mir nur die "Das ist aber kein C++-Meckermecker"-Reaktion 😉

    Und ich dachte immer die wären Teil des C++ Standards und in cstdio zu finden 😮 😮
    Danke für die Aufklärung!



  • Echt nich?? schrieb:

    Und ich dachte immer die wären Teil des C++ Standards und in cstdio zu finden 😮 😮

    Dass etwas aus historischen Gründen bzw. Kompatibilitätsgründen Teil des Standards ist, heißt nicht dass man es in C++ benutzen sollte. C-FILEs gehören zu den Dingen die man in C++ nicht braucht, bestenfalls vorsichtig benutzen sollte (wenn denn überhaupt) und nicht kennen muss um C++ zu können.



  • pumuckl schrieb:

    Echt nich?? schrieb:

    Und ich dachte immer die wären Teil des C++ Standards und in cstdio zu finden 😮 😮

    Dass etwas aus historischen Gründen bzw. Kompatibilitätsgründen Teil des Standards ist, heißt nicht dass man es in C++ benutzen sollte. C-FILEs gehören zu den Dingen die man in C++ nicht braucht, bestenfalls vorsichtig benutzen sollte (wenn denn überhaupt) und nicht kennen muss um C++ zu können.

    Ich finde die nicht so schlecht und haben auch ihre Daseinsberechtigung, da sie etwas ganz anderes als die C++ Streams tun. Und schwerer zu handhaben als die fstreams sind sie auch nicht (für das Einlesen von Binärdaten natürlich, wenn man explizit streams will ist das natürlich etwas ganz anderes).
    Den Puffer muss man übrigens auch nicht selbst verwalten (du weißt das, der TO wohl eher nicht), da std::vector<T> auch explizit über ein T* angesprochen werden kann und damit auch in C-APIs übergeben werden kann, kurz: malloc() für Puffer braucht man in C++ nicht wenn man mit C-APIs arbeitet.



  • Echt nich?? schrieb:

    Ich finde die nicht so schlecht und haben auch ihre Daseinsberechtigung, da sie etwas ganz anderes als die C++ Streams tun.

    Das musst Du erläutern. Ich dachte bisher immer, beide Streamgruppen böten (hauptsächlich) Funktionen für die I/O von Text und Funktionen für die I/O von Binärdaten.



  • Himmel!!! Ich wollte damit keinen Glaubenskrieg auslösen. Das überlasse ich lieber den Pinguinen und Windoofies. 😉

    Ich habe lediglich ein Tutorial durchgemacht, das das Filehandling im C-Stil verwendet. Aber gut. Jetzt bin ich im Bilde. Danke.

    Kurz um: Ich habe mir fstreams angeschaut (read - C++ Reference)und hier passiert genau das Gleiche. Erst

    cout << string (pchrBuffer, lngFileLength) << endl;
    

    brachte das richtige Ergebnis.

    Und so hab ich es gelöst:

    #include <fstream>
    #include <iostream>
    #include <string>
    
    using namespace std;
    
    int main()
    {
    	ifstream ifsFile;
    	long lngFileLength;
    	char * pchrBuffer;
    
    // open file
    	cout << "INFO\tOpen file" << endl;
    	ifsFile.open ("C:\\tmp\\Test.txt", ios::binary);
    	if (!ifsFile.is_open())
    	{
    		// error: File not found 
            cout << "ERROR\tFile not found!" << endl; 
            return 1;
    	}
    
    // get the length of file
    	ifsFile.seekg (0, ios::end);
    	lngFileLength = ifsFile.tellg();
    	ifsFile.seekg (0, ios::beg);
    	cout << "INFO\tFile length:\t" << lngFileLength << endl;
    
    // read file
    	cout << "INFO\tRead file" << endl;
    	pchrBuffer = new char[lngFileLength];
    	ifsFile.read(pchrBuffer, lngFileLength);
    
    // close file
    	cout << "INFO\tClose file" << endl;
    	ifsFile.close();
    
    // show file content 
    	cout << "INFO\tFile content:" << endl; 
    	cout << "--------------------------------------------------------------" << endl; 
    	cout << string (pchrBuffer, lngFileLength) << endl; 
    	cout << "--------------------------------------------------------------" << endl; 
    
    // terminate buffer 
    	free (pchrBuffer); 
    
    // exit program
    	return 0;
    }
    

    Gibt es hier noch Anmerkungen? Ist irgendwas nicht so astrein? Wäre dankbar für jede Kritik. Würde gern wissen, wie es "sicherer" geht. Und ein string-Typ ist mir ja auch viel lieber. 🙂

    Gruß und gute Nacht
    Johnson



  • Benutz anstatt free unbedingt die C++ Funktionen. In diesem Fall delete[]

    Das Vermischen von C/C++ kann bei Speicherreservierung und Speicherfreigabe sonst große Probleme hervorrufen.

    Übrigens gibt es für C++ Code ein eigenes Tag. Dort werden dann sogar die Keywords hervorgehoben.
    Hier nochmal der von dir gepostete Code, mit den richtigen Code-Tags:

    #include <fstream>
    #include <iostream>
    #include <string>
    
    using namespace std;
    
    int main()
    {
        ifstream ifsFile;
        long lngFileLength;
        char * pchrBuffer;
    
    // open file
        cout << "INFO\tOpen file" << endl;
        ifsFile.open ("C:\\tmp\\Test.txt", ios::binary);
        if (!ifsFile.is_open())
        {
            // error: File not found
            cout << "ERROR\tFile not found!" << endl;
            return 1;
        }
    
    // get the length of file
        ifsFile.seekg (0, ios::end);
        lngFileLength = ifsFile.tellg();
        ifsFile.seekg (0, ios::beg);
        cout << "INFO\tFile length:\t" << lngFileLength << endl;
    
    // read file
        cout << "INFO\tRead file" << endl;
        pchrBuffer = new char[lngFileLength];
        ifsFile.read(pchrBuffer, lngFileLength);
    
    // close file
        cout << "INFO\tClose file" << endl;
        ifsFile.close();
    
    // show file content
        cout << "INFO\tFile content:" << endl;
        cout << "--------------------------------------------------------------" << endl;
        cout << string (pchrBuffer, lngFileLength) << endl;
        cout << "--------------------------------------------------------------" << endl;
    
    // terminate buffer
        free (pchrBuffer);
    
    // exit program
        return 0;
    }
    


  • Anstelle des new char[..] würde ich ein std::vector<char> vorziehen.
    Simon



  • Wenn du die Dateien nicht im Binärmodus aufmachst ("rb"), machst du sie im Textmodus auf ("r"). Unter Windows macht das nen Unterschied.
    Und wenn du sie im Textmodus aufmachst, dann kann es sein, dass fread WENIGER liefert, als Bytes in der Datei drinstehen (weil CR-LF zu nur LF umgewandelt wird).

    Da du in deinem Programm (im ersten Beitrag) nur auf gleichheit testest, vermute ich dass du genau diesen Phänomen beobachtet hast.



  • @BBBB: delete[] Stimmt! Da war was! Hab's geändert. Danke.

    @hustbaer: Ich gebe mir doch die Größe aus. In meiner Test.txt steht folgendes drin:

    Zeile 1
    Zeile 2
    
    Zeile 4
    

    Das sind mit CR-LF 27 Bytes. Das ist im HEX-Editor auch zu sehen.

    Offne ich die Datei mit "rb". Steht nach fread folgendes im chrBuffer:
    "Zeile 1CRLFZeile 2CRLFCRLRZeile 4ýýýý««««««««þ"
    (CRLF steht nicht wirklich drin. Ich habe hier nur die Zeichen benannt)

    Ich glaube, der Hund ist schon bei "malloc" begraben, denn er macht den Buffer nicht, wie ich erwartete 27 Bytes groß, sonder 40 Bytes groß: "ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍýýýý««««««««þ" Das Gleiche macht auch "new char[lngFileLength]".
    Nur verstehe ich nicht warum. Kann mir da jemand Nachhilfe geben?



  • Wenn Du 27 Bytes anfordest, nimm die 27 Bytes die Du kriegst und gib sie anständig wieder zurück.

    Was die Runtime noch für Debugging Patterns und andere Infos hinten dran schreibt ist ihre Sache.

    Simon

    Edit
    Ich vermute ausserdem stark, dass Du Aufgrund der Tatsache, dass Du im Debugger "ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍýýýý««««««««þ" siehst, annimmst, dass eben 40 Bytes (o.ä.) reserviert wurden. In der Tat siehst Du aber einfach das was Dir der Debugger anzeigt und das ist bei einem char* einfach der Inhalt vom Pointer bis zum ersten '\0'. Und das was Du siehst ist der nicht- initialisierte Speicherinhalt, der ev. mit einem Debug Pattern belegt wurde.



  • @Johnson:
    Du musst schon ein null-byte anhängen, wenn du den Zeiger auf den Puffer als C-String verwenden willst. C-Strings sind halt nullterminiert.
    Die Funktionen können ja nicht wissen wie lange der String sein soll...


Anmelden zum Antworten