Datei auf Dateiende prüfen



  • Hallo,

    bei dem gezeigten Programm-Code habe ich das Problem: das die letzte Zeile doppelt angezeigt wird.

    Also anstatt zwei Zeilen werden drei Zeilen angezeigt.

    Ich denke mal das Problem liegt auf der Abfrage "Datei-Ende" (while(!feof(stream).

    Wie kann ich die "while-Schleife" beenden das nur die zwei Zeilen angezeigt werden?

    // Datei_fscanf_01.cpp : Diese Datei enthält die Funktion "main". Hier beginnt und endet die Ausführung des Programms.
    // https://docs.microsoft.com/de-de/cpp/c-runtime-library/reference/fscanf-fscanf-l-fwscanf-fwscanf-l?view=msvc-170

    
    // This program writes formatted
    // data to a file. It then uses fscanf to
    // read the various data back from the file.
    
    #include <stdio.h>
    #include <iostream>
    
    #define _CRT_SECURE_NO_DEPRECATE
    #pragma warning (disable : 4996)
    
    using namespace std;
    
    FILE* stream;
    
    int main(void)
    {
        long l;
        float fp;
        int i;
        char s[81];
        char c;
    
        if (fopen_s(&stream, "fscanf.out", "w+") != 0)
            printf("The file fscanf.out was not opened\n");
        else
        {
            fprintf(stream, "%d %s %ld %f%c\n", 23, "a-string",
                65000, 3.14159, 'x');
            fprintf(stream, "%d %s %ld %f%c\n", 24, "b-string",
                65001, 3.14159, 'y');
            // Security caution!
            // Beware loading data from a file without confirming its size,
            // as it may lead to a buffer overrun situation.
    
            // Set pointer to beginning of file:
            fseek(stream, 0L, SEEK_SET);
     
            while(!feof(stream))
            {
                // Read data back from file:
                fscanf(stream, "%2d", &i);   
                fscanf(stream, "%80s", s);
                fscanf(stream, "%ld", &l);
                fscanf(stream, "%f", &fp);
                fscanf(stream, "%c", &c);
                
                // Output data read:
                printf("Integer %2d\n", i);
                printf("String  %10s\n", s);
                printf("Long    %5ld\n", l);
                printf("Float   %7.5f\n", fp);
                printf("Zeichen %c\n", c);
                printf("\n");
            }
            fclose(stream);
            cout << "Eingabe erwartet";
            cin.get();
        }
    }
    

    IDE= Visual Studio 2022

    MfG

    Juergen B.



  • Welche Programmiersprache möchtest du benutzen? Das sieht nach C aus, ist aber im C++ Forum gepostet. Hast du dich im Forum geirrt oder möchtest du C++ programmieren?



  • Hallo,
    da habe ich mir wohl von der genannten Webseite fscanf das falsche Beispiel geholt (C-Code).
    Ich habe das Beispiel aber unter Visual-Studio C++ compiliert.
    Also möchte ich das unter C++ programmieren.

    MfG

    Juergen B.



  • In C++ benutzt man I/O streams statt der C-Funktionen fprinf/fscanf. Für strings gibt´s den Datentyp std::string, man hantiert nicht mehr fehleranfällig mit rohen char-Arrays.
    Und wenn du schon strukturierte Daten lesen und schreiben möchtest, warum organisierst du sie dann nicht auch programmintern?

    #include <string>
    #include <iostream>
    #include <fstream>
    
    struct MyData
    {
       long LongData = 0;
       float FloatData = 0.0f; 
       int IntegerData = 0;
       std::string StringData;
       char CharData = 0;
    
       MyData() = default;
    };
    
    std::ostream& operator<<( std::ostream& os, MyData const& data )
    {
       // to do: Daten schreiben
       return os;
    }
    
    std::istream& operator>>( std::istream& is, MyData& data )
    {
       // to do: Daten lesen
       return is;
    }
    
    void write_test( std::string const& filename )
    {
       MyData data;
       std:ofstream ofs( filename );
       ofs << data;
    }
    
    void read_test( std::string const& filename )
    {
       MyData data;
       std::ifstream ifs( filename );
       ifs >> data;
    }
    
    int main()
    {
       std::string const filename = "<Dateiname>";
       write_test( filename );
       read_test( filename );
    }
    

    So könnte ein Grundgerüst aussehen, ohne Netz und doppelten Boden. Überprüfung der gelesenen Daten findet nicht statt, aber wie man das macht kannst du dir relativ schnell anlesen. Genauso wie das tatsächliche Lesen und Schreiben der Datenelemente.


  • Mod

    Egal ob C oder C++ (oder egal welche andere Sprache außer ein paar Exoten) gilt: Man weiß erst dann, wann ein Dateiende erreicht ist, wenn man erfolglos versucht hat, über das Ende zu lesen. Nicht, wenn man erfolgreich vor dem Ende steht. Das liegt an allerlei Dingen, aber du kannst dir einfach vorstellen, dass eine Datei ja noch wachsen könnte, während du liest, und feof kann nicht in die Zukunft gucken, ob das passiert bevor du das nächste Mal lesen würdest.

    Die Logik

    while(Dateiende nicht erreicht)
    {
        lese; 
        verarbeite;
    } 
    

    ist daher in jeder Sprache falsch. Es muss lauten

    while(lesen erfolgreich)
    {
        verarbeite;
    }
    

    In C würde man typischerweise so etwas schreiben wie

    while(fscanf(stream, format, &variable))
    {
        verarbeite(variable);
    }
    

    was den Rückgabewert von fscanf auswertet, welcher den Erfolg einer Leseaktion anzeigt.
    In C++ ist die Idee die gleiche:

    while(stream >> variable)
    {
        verarbeite(variable);
    }
    

    Man muss natürlich die gesamte Leseaktion in einen einzigen Ausdruck gepackt bekommen, und du hast hier mehrere Leseaktionen. Das ist aber weder in C noch C++ ein Problem. In C könnte man einen komplexeren Formatstring mit mehreren gelesenen Variablen benutzen, oder das Lesen in einer Funktion auslagern mit passendem Rückgabewert. In C++ sind Leseaktionen von Natur aus beliebig aneinander reihbar:

    while(stream >> var1 >> var2 >> var3)
    {
        verarbeite(var1, var2, var3);
    }
    


  • @jbaben Das Problem ist, dass nach dem lesen von c (Zeile 46) noch das '\n' in der Datei steht. Somit ist die Datei noch nicht zuende und feof noch nicht wahr.

    Dann werden alle Leseoperationen versucht, sind nicht erfolgreich und die alten Daten stehen noch in den Variablen.

    Wenn du den ersten Lesebefehl in die Bedingung der while-Schleife nimmst, sollte es gehen:

    while(1 == fscanf(stream, "%2d", &i))
            {
                // Read data back from file:
     //           fscanf(stream, "%2d", &i);
    

    Aber noch was Anderes: Das Zahlenliteral 65000 ist ein int (bei 16-Bit-Systemen ein unsigned int).
    Du möchtest das bei printf als long übergeben.
    Solange die Größe von long und int identisch ist, funktioniert das. Wenn nicht, gibt es undefined behavior, was so ziemlich Alles bewirken kann.
    Mit 65000L wird aus dem Literal ein long

    printf und scanf sind nicht typsicher, im Gegensatz zu den Stgreamoperatoren von C++.



  • Hallo,

    1. der Hinweis von DirkB (while (1 == fscanf(stream, "%2d", &i)) hat in meinem Beispiel funktioniert.

    2. DocShoe:
      a. das gezeigte ist ja nur ein Beispiel
      b. im eigentlichen Programm benutze ich natürlich eine Struktur für die Daten
      c. da ich mein Programm in C++ programmieren möchte, werde ich mein Beispiel überarbeiten und den I/O stream verwenden

    3. Vielen Dank für Eure schnelle Hilfe, hat mich ersteinmal auf den richtigen Weg gebracht.

    MfG

    Juergen B.



  • @jbaben Es wäre sinnvoll, dass du verstehst, warum das (so) funktioniert.



  • @DirkB
    Hallo,
    ja das ist natürlich sinnvoll: "fscanf": Rückgabewert von 0 gibt an, dass keine Felder zugewiesen wurden.

    MfG

    Juergen B.



  • @DocShoe
    Hallo,
    das gezeigte Beispiel von Dir funktioniert leider bei mir nicht (kompiliert mit C++ Visual_Studio 2022).
    Die Daten werden nicht in der entsprechenden Datei gespeichert und somit auch nicht angezeigt.

    MfG

    Juergen B.



  • Hast du denn auch die beiden // to do: ... ausprogrammiert?



  • @Th69
    Hallo,
    nein hatte ich nicht.
    Aber wenn ich das auskommentiere (die beiden // entferne) erhalte ich die Meldung:
    "E0020 Der Bezeichner "to" ist nicht definiert"

    MfG

    Juergen B.





  • Wir hatten damals jemanden, der in der Uni (2. Semester) in den Informatik-Hausaufgaben einen Quellcode mit Folgendem (sinngemäß) abgegeben hat:

    // todo: hier musst du noch deinen Namen und deine Matrikelnummer eintragen 
    

    Derjenige bekam damals in der Vorlesung den "P2 Darwin-Award" 🙂



  • @DirkB
    Hallo,

    das ist mir jetzt aber peinlich, das ich so falsch reagiert habe.
    Natürlich weiss ich was eine To-Do-Liste ist, da muss ich wohl total geträumt haben.
    Anderseits bin ich aber davon ausgegangen das Beispiel ist komplett.

    MfG

    Juergen B.



  • @jbaben

    Das Motto ist Hilfe zur Selbsthilfe. Man bekommt hier selten kompletten, lauffähigen Code, vielmehr soll der Fragesteller in die richtige Richtung geschubst werden und sich den Rest dann selbst erarbeiten. Durch Copy & Paste lernt man nix.



  • Hallo,
    ja ist schon ok.
    Hier das komplette Beispiel:

    #include <string>
    #include <iostream>
    #include <fstream>
    
    using namespace std;
    
    struct MyData
    {
    	public:
    		long LongData = 1;
    		float FloatData = 2.3f;
    		int IntegerData = 4;
    		string StringData = "Test";
    		char CharData = '?';
    
    	MyData() = default;
    };
    
    ostream& operator<<(ostream& os, MyData const& data)
    {
    	// to do: Daten schreiben
    	os << data.LongData << ",'" << data.FloatData << "','" << data.IntegerData << "','" << data.StringData << "'," << data.CharData << endl;
    	return os;
    }
    
    istream& operator>>(istream& is, MyData& data)
    {
    	// to do: Daten lesen
    	is >> data.LongData >> data.FloatData >> data.IntegerData >> data.StringData >> data.CharData;
    	return is;
    }
    
    void write_test(string const& filename)
    {
    	MyData data;
    	ofstream ofs;
    	ofs.open(filename,ios::app);
    	ofs << data;
    	//ofs << data.LongData << endl;
    	ofs.close();
    }
    
    void read_test(string const& filename)
    {
    	MyData data;
    	ifstream ifs;
    	ifs.open(filename,ios::in);
    	ifs >> data;
    	cout << data << endl;
    	ifs.close();
    }
    
    int main()
    {
    	string const filename = "daten";
    	write_test(filename);
    	read_test(filename);
           cout << "Eingabe erwartet";
    	cin.get();
    }
    

    MfG

    Juergen B.



  • Das Einlesen paßt aber nicht 1:1 zu den geschriebenen Daten.
    Initialisiere mal MyData data in Zeile 45 so:

    MyData data = { 0L, 0.f, 0, "", '*'};
    

    Und lass dann das Programm laufen.

    PS: Warum ios:app beim Schreiben der Datei?


Anmelden zum Antworten