Dateiende erkennen



  • Hallo zusammen,
    das ist mein erster Eintrag hier im Forum.
    Ich hoffe ihr könnt mir helfen und das meine Frage nicht all zu dumm ist 😉

    Ich versuche gerade ein Programm zu schreiben in dem ich ein Struct aus einer Datei lesen möchte.
    Mein Problem dabei ist aber, dass das zu lesende Struct keine bestimmte Anzahl an "Zeilen" hat und ich somit vorher heraus finden muss wie viele "Zeilen" ich auslesen muss.
    Habe es Erfolglos so versucht(was auch glaube ich quatsch ist):

    int i=0;
    	FILE *stream;
    	if (fopen_s(&stream, "C:\\Pfad\\speicher.dat", "rb") != NULL)
    	{
    		printf("Datei kann nicht geöffnet werden!\n");
    		return(1);
    	}
    
    	while (!feof(stream))
    	{
    		fread(tabelle, sizeof(artikel), i, stream);
    		i++;
    	}
    
    	fclose(stream);
    

    Vielen Dank schon mal 🙂


  • Mod

    Das sieht mir doch sehr stark nach C aus. Bist du sicher, ob du wirklich C++/CLI machen möchtest? C, C++, C++/CLI, C#, etc. haben zwar alle ähnliche Namen, sind aber doch unterschiedliche Sprachen, mit ganz unterschiedlichen Denkweisen. Eine Antwort für C++/CLI würde dir kaum weiterhelfen, wenn du in Wirklichkeit C machst.

    In egal welcher dieser Sprachen ist eine Schleife der Form

    while(!eof)
    {
      lies(daten);
      verarbeite(daten);
    }
    

    immer logisch falsch. End of File ist ein Zustand, der erreicht wird, nachdem über das Ende einer Datei gelesen wurde, nicht bevor eventuell über das Ende gelesen werden könnte. Das heißt für deinen jetzigen Code: Am Ende der Datei wird die letzte Leseoperation fehlschlagen; du verarbeitest dann ungeprüft Mülldaten; und erst danach bemerkst du das Dateiende. Falls du das irgendwo so beigebracht bekommen hast, dann taugt dein Lehrer nichts und du solltest besser auch nichts anderes mehr von ihm lernen. Die einzige Sprache, die mir einfällt, bei der obiges funktioniert, ist Pascal.

    Eine allgemeine Antwort für dein Problem, die auf alle Sprachen angewendet werden kann:

    • Falls alle Datensätze exakt gleich große sind, kann man ausrechnen, wie viele Datensätze es gibt, indem man die Größe der Datei durch die Größe eines Datensatzes teilt. Dann holt man sich genug Speicherplatz für alle Datensätze und liest alles ein. Es sieht so aus, als wären bei dir alle Datensätze gleich groß.
    • Falls Datensätze unterschiedlich groß sind, liest man eben einen nach dem anderen aus und holt sich bei Bedarf mehr Platz zum Speichern der Datensätze. Das funktioniert natürlich genau so gut für Datensätze, die alle gleich groß sind, und hat praktisch keine wesentlichen Nachteile gegenüber der Methode aus dem vorherigen Absatz.

    Wie das im Detail funktioniert, ist je nach Sprache unterschiedlich.



  • Hallo Sepp,
    vielen Dank für deine ausführliche Antwort.
    Natürlich schreibe ich in C, irgendwie bin ich gestern wohl durcheinander gekommen oder es war schon zu spät 😮 .
    Und ja du hast richtig erkannt das alle Datensätze gleich groß sind.
    Ich habe gehofft das ich solange die Zeilen zählen kann, bis keine Datensätze mehr vorhanden sind. Aber du hast natürlich recht, meine Schleife kann so nicht Funktionieren.



  • Dieser Thread wurde von Moderator/in SeppJ aus dem Forum C++/CLI mit .NET in das Forum C (alle ISO-Standards) verschoben.

    Im Zweifelsfall bitte auch folgende Hinweise beachten:
    C/C++ Forum :: FAQ - Sonstiges :: Wohin mit meiner Frage?

    Dieses Posting wurde automatisch erzeugt.


  • Mod

    das_blinker schrieb:

    Und ja du hast richtig erkannt das alle Datensätze gleich groß sind.
    Ich habe gehofft das ich solange die Zeilen zählen kann, bis keine Datensätze mehr vorhanden sind.

    Möglich wäre das, aber ein bisschen umständlich. Denn das Zählen der Zeilen ist gleichbedeutend mit Auslesen der Datei, also kannst du auch gleich richtig auslesen und dabei mitzählen, wie viele Datensätze du gelesen hast. Speicher dynamischer Größe zum Speichern von Daten kann man in C mittels der malloc-Funktion (oder einer ihrer Varianten) erhalten. Mittels realloc kann die Größe dieses Bereichs dann auch nachträglich noch verändert werden. Das ist der entscheidende Trick, wieso man die Anzahl der Datensätze nicht unbedingt im Voraus kennen muss.

    Oder aber du holst dir die Größe der Datei und teilst diese durch die Größe eines einzelnen Datensatzes. Die Größe einer Datei kann man erhalten, ohne die Datei komplett auszulesen. Entweder durch Betriebssystemfunktionen (stat ist auf praktisch allen Systemen vorhanden und kompatibel) oder indem du ans Ende der Datei springst und dir den Wert des Lesezeigers holst (fseek/ftell). Dann holst du dir (wieder mittels malloc) genügend Speicher für alle Datensätze und kannst dann mittels fread¹ in einem Rutsch alles auslesen.

    ¹: Bei der Gelegenheit fällt mir übrigens auf, dass du das fread in deinem Beispiel falsch benutzt. Guck dir noch einmal die Parameter und ihre Bedeutung an.



  • du könntest auch steuerzeichen verwenden, also z.b. 00



  • Für was?



  • Hallo SeppJ

    Was ist mit

    while ( (c = fgetc(fz))  != EOF)
    {
      lies(daten);
      verarbeite(daten);
    }
    

    das ginge eher oder?


  • Mod

    rustyoldguy schrieb:

    Hallo SeppJ

    Was ist mit

    while ( (c = fgetc(fz))  != EOF)
    {
      lies(daten);
      verarbeite(daten);
    }
    

    das ginge eher oder?

    Nein, dein Vorschlag produziert doch exakt die gleiche Problematik.



  • Dann bleit dir nix anderes übrig, als etwa mit

    currentposition = ftell(fz);  
    filelength = lseek(fh, 0, SEEK_END);  
    position = lseek(fh, currentposition, SEEK_SET);
    

    zuerst die Dateilänge zu bestimmen, um dann
    die Anzahl der Datensätze zu berechnen, welche
    dann den Lesevorgang mit einer for-Schleife
    vornehmen


  • Mod

    Tja, ein unlösbares Problem also. Wenn es doch bloß möglich wäre, festzustellen, ob eine Leseaktion erfolgreich war. Sollte mal jemand erfinden, so etwas.

    🙄



  • das_blinker schrieb:

    Und ja du hast richtig erkannt das alle Datensätze gleich groß sind.

    Da du keine Zeilen benutzt sondern Datensaetze, die auch noch immer gleich groß sind, waren deine Ansätze
    - Verwendung einer Struktur
    - Verwendung von fread
    schon richtig und wenn du das dir alleine erarbeitet hast spricht einiges für deine Auffassungsgabe, es hapert bei dir nur noch im Verständnis von Zeigern und realloc.
    Lass das fopen_s sein, jeder vernünftige Mensch benutzt fopen zumal du fopen_s falsch auswertest.

    typedef struct Artikel Artikel;
    	Artikel *tabelle = 0, artikel;
    	int i=0;
            ...
    
    	while (fread(&artikel, sizeof(artikel), 1, stream)==1)
    	{		
    		i++;
    		tabelle = realloc(tabelle,i*sizeof*tabelle);
    		tabelle[i-1] = artikel;
    	}
    
    	fclose(stream);
    	free(tabelle);
    

    Mit realloc vergrößerst du also immer bei Bedarf deine tabelle um einen Elementplatz, am Ende free nicht vergessen.
    Je nach Implementierung arbeitet realloc unterschiedlich "optimal", für deine Zwecke und wenige tausend Aufrufe meistens völlig aussreichend, bei Bedarf gibt es einfache manuelle Optimierungsvarianten.


Log in to reply