Probleme mit Fstream | Datei bleibt leer



  • Hallo zusammen,

    Ich muss für die Schule ein Programm schreiben welches ein Gedicht erfassen, speichern und ändern kann.
    Soweit klappt alles ganz gut.

    Jedoch funktioniert das Speichern nicht, die Datei wird zwar erstellt aber bleibt leer. Ich habe versucht Fehler abzufangen mit "cerrno" und habe als Nachricht nur "No Error" bekommen.

    Syntaktisch ist eigentlich alles richtig.
    Desweiteren scheint es bei einem Freund von mir zu funktionieren, bei einem weiteren wiederum nur teilweise und bei mir gar nicht.
    Ich hoffe hier Hilfe zu finden. 🙂

    Hier meine Funktion zum Speichern (TStrophe *TS ist eine Struktur):

    void GedichtSpeichern(TStrophe *TS, string &Path, int *anzahlStrophen, int *anzahlVerse, int *anzahlVerseGesamt) {
    	fstream flGedicht;
    	flGedicht.open(Path.c_str(), ios::app);
    	//Wenn anzahlStrophen oder anzahlVerse gleich 0, macht speichern keinen Sinn
    	if (*anzahlStrophen != 0 || *anzahlVerseGesamt != 0) {
    		if (flGedicht) { //Wenn Datei existiert
    			for (int strophe = 0; strophe < *anzahlStrophen; strophe++) {
    				flGedicht << "-----------------------------------" << '\n';
    				flGedicht << "Strophe " << strophe + 1 << '\n';
    				if (!flGedicht.fail() && !flGedicht.bad()) {
    					//Hier wird nun anzahlVerseGesamt aus Funktion Input benutzt
    					//Damit die for-schleife alle verse durchgehen kann und nicht den letzten Input von *anzahlVerse uebernimmt
    					for (int vers = 0; vers < *anzahlVerseGesamt; vers++) {
    						flGedicht << TS->Verse[vers] << '\n';
    
    					}
    				} else {
                                            //Hier kommt die Fehlermeldung "no error"
    					cerr << strerror(errno) << endl;
    					break;
    				}
    			}
    			flGedicht.close();
    		} else {
    			cerr << strerror(errno) << endl;
    		}
    	} else {
    		cerr << "Anzahl der Strophen und oder Anzahl der Verse duerfen nicht gleich 0 sein!" << endl;
    	}
    	flGedicht.close();
    }
    


  • Wird denn überhaupt eine Stelle erreicht, an der in die Datei geschrieben wird? Benutze einen Debugger oder Debugausgaben nach cerr um das festzustellen.

    Warum übergibst du anzahlStrophen als Zeiger?



  • Hi,

    mit deiner Parameterübergabe als Pointer liest sich das ganze ein wenig wie C Code mit ein paar C++ Featuren (insbesondere fstream).

    Wenn du nur Ausgaben machst, würde ich direkt "ofstream" nutzen.

    Wenn du unterschiedliche Ergebnisse bekommst, spricht das dafür, dass dein Pointer auf einen nicht reservierten Speicherbereich zeigt. Was da genau schief läuft, kann ich/wir dir aber nur sagen, wenn du uns zeigst von wo die Funktion aufgerufen wird und wie die Datenstruktur befüllt wird.

    Warum arbeitest du nicht mit std::vectorstd::string für deine Verse und einem Vektor aus Versen für die Strophe? Dann könntest du schön mit Iteratoren da durch marschieren und müsstest dich nicht selbst um die Speicherbereiche kümmern.



  • manni66 schrieb:

    Wird denn überhaupt eine Stelle erreicht, an der in die Datei geschrieben wird? Benutze einen Debugger oder Debugausgaben nach cerr um das festzustellen.

    Warum übergibst du anzahlStrophen als Zeiger?

    Mit dem Debugger wird ab der Abfrage " if (!flGedicht.fail() && !flGedicht.bad()) { " nur der Fehler "no error" ausgegeben und die 2 Zeilen davor, wo eigentlich die Anzahl der Strophen stehen sollten, werden anscheinend ignoriert. Also bleibt die Datei Leer.

    Weiß nicht genau warum ich anzahlStrophen als Zeiger übergebe, aber macht ja keinen unterschied ob ich dafür Call by Value oder Call by Reference benutze.



  • Schlangenmensch schrieb:

    Hi,

    mit deiner Parameterübergabe als Pointer liest sich das ganze ein wenig wie C Code mit ein paar C++ Featuren (insbesondere fstream).

    Wenn du nur Ausgaben machst, würde ich direkt "ofstream" nutzen.

    Wenn du unterschiedliche Ergebnisse bekommst, spricht das dafür, dass dein Pointer auf einen nicht reservierten Speicherbereich zeigt. Was da genau schief läuft, kann ich/wir dir aber nur sagen, wenn du uns zeigst von wo die Funktion aufgerufen wird und wie die Datenstruktur befüllt wird.

    Warum arbeitest du nicht mit std::vectorstd::string für deine Verse und einem Vektor aus Versen für die Strophe? Dann könntest du schön mit Iteratoren da durch marschieren und müsstest dich nicht selbst um die Speicherbereiche kümmern.

    Meine Datenstruktur wird befüllt in dem ich die Anzahl der Strophen/Verse eingebe.
    Danach lege ich die größe des meines Verses fest mit:

    TS->Verse = new (nothrow) string[*anzahlVerse];
    

    Die Eingabe mache ich dann in einer for-schleife:

    for (int vers = 0; vers < *anzahlVerse; vers++) {
    			cout << "Inhalt von Vers " << vers + 1 << ":" << endl;
    			getline(cin, TS->Verse[vers]);
    
    		}
    

    Das mit dem std::vector... liegt daran, dass ich mich damit noch gar nicht beschäftigt habe. Ausserdem haben wir das noch nie im Unterricht besprochen.



  • Ich habe grade nochmal in die Doku geschaut. Wenn du fail() überprüfst brauchst du bad() nicht (zumindest bei der && Verknüpfung), da wenn bad()==true auch fail == true() (http://www.cplusplus.com/reference/ios/ios/bad/)

    Wenn du call by reference machen möchtest würde ich

    void GedichtSpeichern(TStrophe *TS, string &Path, int& anzahlStrophen, int&  anzahlVerse, int& anzahlVerseGesamt)
    

    empfehlen, da eine Referenz kein Nullpointer sein kann und du dir dann das dereferenzieren sparen kannst.

    Edit:
    Etwas kompletterer Code wäre hilfreich, um eventuelle Gültigkeiten von Variablen zu überprüfen.



  • Schlangenmensch schrieb:

    Ich habe grade nochmal in die Doku geschaut. Wenn du fail() überprüfst brauchst du bad() nicht (zumindest bei der && Verknüpfung), da wenn bad()==true auch fail == true() (http://www.cplusplus.com/reference/ios/ios/bad/)

    Wenn du call by reference machen möchtest würde ich

    void GedichtSpeichern(TStrophe *TS, string &Path, int& anzahlStrophen, int&  anzahlVerse, int& anzahlVerseGesamt)
    

    empfehlen, da eine Referenz kein Nullpointer sein kann und du dir dann das dereferenzieren sparen kannst.

    Habe versucht die fail()- und bad()Abfrage zu ändern und auch nur eines von beiden zu benutzen. Dies hat leider nichts geändert und es kommt immer noch dieselbe Fehlermeldung (no error).

    Schlangenmensch schrieb:

    Edit:
    Etwas kompletterer Code wäre hilfreich, um eventuelle Gültigkeiten von Variablen zu überprüfen.

    Vielleicht hilft meine Eingabe-Funktion...
    Hier der Inhalt:

    bool resStrophen = true;
    	bool resVerse = true;
    	//Strophen
    	do {  //Schleife falls falsche Eingabe
    		cout << "Wie viele Strophen hat ihr Gedicht?" << endl;
    		cin >> *anzahlStrophen;
    
    		while (cin.fail()) {
    			cin.clear();
    			cin.ignore(100, '\n');
    			cout << "Bitte nur Zahlen eingeben!" << endl;
    			cin >> *anzahlStrophen;
    		}
    		//Nicht mehr als 5 und nicht weniger als 1 Strophe
    		if (*anzahlStrophen > 5 || *anzahlStrophen == 0) {
    
    			cout << "Bitte 1 oder 5 Strophen angeben " << endl;
    			resStrophen = true;
    		} else {
    			TS->Strophen = new (nothrow) string[*anzahlStrophen];
    			resStrophen = false;
    		}
    	} while (resStrophen == true);
    
    	for (int strophe = 0; strophe < *anzahlStrophen; strophe++) {
    		do { //Schleife falls falsche Eingabe
    			cout << "Strophe : " << strophe + 1 << endl;
    			cout << "Wie viele Verse hat Strophe " << strophe + 1 << "?" << endl;
                            //Verse werden erst hier eingegeben, damit jede Strophe beliebig viele Verse haben kann
    			cin >> *anzahlVerse;
    			while (cin.fail()) {
    				cin.clear();
    				cin.ignore(100, '\n');
    				cout << "Bitte nur Zahlen eingeben!" << endl;
    				cin >> *anzahlVerse;
    			}
    			//Nicht mehr als 10 und nicht weniger als 1 Vers
    			if (*anzahlVerse > 10 || *anzahlVerse == 0) {
    				cout << "Bitte 1 oder 10 Verse angeben " << endl;
    				resStrophen = false;
    				resVerse = true;
    			} else {
    				*anzahlVerseGesamt += *anzahlVerse;
    				TS->Verse = new (nothrow) string[*anzahlVerse];
    				resStrophen = false;
    				resVerse = false;
    			}
    		} while (resVerse == true);
    		for (int vers = 0; vers < *anzahlVerse; vers++) {
    			cin.clear();
    			cin.ignore(100, '\n');
    			cout << "Inhalt von Vers " << vers + 1 << ":" << endl;
    			getline(cin, TS->Verse[vers]);
    
    		}
    	}
    	//Debug-Nachricht
    	cout << *anzahlVerseGesamt << endl;
    	resStrophen = false;
    	resVerse = false;
    


  • Dass ist deine Eingabe Funktion? Und der wird ein Pointer auf TS übergeben?
    Wie ist das Struct definiert?

    Bei dir hat TS ein Array Strophen und ein Array Verse. Sollte nicht jede Strophe aus mehreren Versen bestehen?

    TS->Verse = new (nothrow) string[*anzahlVerse];
    

    Du überschreibst mit jeder Strophe TS->Verse
    Was du vermutlich möchtest ist sowas wie

    struct strophe{
    string *verse; 
    }
    struct gedicht{
    strophe* s;
    }
    TS->s[0]=new (nothrow) string[*anzahlVerse];
    

    Achtung: Beispiel nicht vollständig



  • Mehrere Dinge.

    1. Das rumgepointere für int-Variablen nervt und macht den Code schwerer lesbar. Lass das. Kein int* in deinem Programm! Zumindest nicht, sofern es einen vernünftigen Grund dafür gibt.

    2. Das Einlesen einer Variablen könntest du in eine Funktion auslagern. Zum Beispiel int readUntilAnswerInRange(istream& is, int from, int to) oder ähnlich. Dann hast du nicht 2x fast identischen Code und kannst auch deine resStrophen/resVerse-Variablen loswerden.

    3. Warum new nothrow? Und wenn z.B. der Speicher voll ist? Wer checkt das Ergebnis? Versuche völlig auf das "new" zu verzichten und nimm stattdessen std::vector!



  • Schlangenmensch schrieb:

    Dass ist deine Eingabe Funktion? Und der wird ein Pointer auf TS übergeben?
    Wie ist das Struct definiert?

    Im header:

    struct TStrophe {
    
    	string *Verse;
    	string *Strophen;
    };
    

    In meiner main Funktion:

    TStrophe TS;
    

    Schlangenmensch schrieb:

    Bei dir hat TS ein Array Strophen und ein Array Verse. Sollte nicht jede Strophe aus mehreren Versen bestehen?

    Bei mir kann jede Strophe aus mehreren Versen bestehen. Ab der 1. for-schleife in der Eingabe-Funktion ist dies zu sehen.

    Schlangenmensch schrieb:

    TS->Verse = new (nothrow) string[*anzahlVerse];
    

    Du überschreibst mit jeder Strophe TS->Verse

    Danke für den Hinweis, ist mir irgendwie noch nicht aufgefallen.
    Jedoch ändert das ganze erstmal nichts an meinem Problem, dass nichts gespeichert wird.


  • Mod

    Was hast du mit all den Zeigern? Das ist nicht nur vollkommen unnötig so, sondern sogar aktiv schädlich. Ich vage mal die Behauptung, dass deine Probleme allein darauf beruhen und magisch verschwinden werden, wenn du sie einfach weg lässt.



  • Und es fehlen immer noch Code Reste. Z.b. wo und wie die Variablen angelegt und die Funktionen aufgerufen werden. Grade bei dem Pointer Wirrwarr kann da dich einiges schief gehen.


Log in to reply