string aus Datei wird bei cout verschluckt / überschrieben



  • Guten Abend,

    ich habe hier eine Textdatei, deren Inhalt exakt bekannt ist, so dass ich mir dachte, dass ich sie ohne Schleife auslese.

    string MonsterName;
    	string sMonsterTP;
    	string sMonsterIniMod;
    
    	ifstream datei;
    	datei.open("Monster.chr", ios::binary|ios::in);
    
    	getline(datei, MonsterName);
    	getline(datei, sMonsterTP);
    	getline(datei, sMonsterIniMod);
    
    	datei.close();
    
    	stringstream Str;
        Str << sMonsterTP;
        int MonsterTP;
        Str >> MonsterTP;
    
    	Str << sMonsterIniMod;
        int MonsterIniMod;
        Str >> MonsterIniMod;
    
    	cout << MonsterName << "Das ist ein Test" << endl;
    

    Der Inhalt der Textdatei ist

    Drache
    100
    10
    5
    

    Dummerweise ist vom "Drache" nichts mehr zu lesen, da "Das ist ein Test" die Ausgabe überschreibt. Kann es sein, dass die Länge des Strings "MonsterName" dem Programm aus irgendeinem Grund nicht bekannt ist und deshalb die Ausgabe nicht zu sehen ist? Wenn ich den "Test" Satz weglasse, wird mir "Drache" korrekt angezeigt.
    Was kann ich tun?



  • Ich habe zwar keine Ahnung, was das Problem ist (Ich vermute es liegt an binary), aber du kannst das alles viel einfacher haben:

    std::string name;
    int tp, ini_mod;
    
    {
        std::ifstream in("Monster.chr"); // wir öffnen im Konstruktor, nennt man RAII
        in >> name >> tp >> ini_mod;
    } // die datei wird für uns dank RAII automatisch geschlossen, .close() unnötig
    

    Beim Einlesen werden alle Leerzeichen, Newlines, Tabulatoren u.ä. übersprungen.

    Was mir dann noch aufgefallen ist: In der Datei stehen zwar 4 Werte, du liest aber nur 3 ein. Den vierten kannst du nun aber einfach dazuschreiben, ist ja nicht schwer.

    Beachte, dass der von mir eingefügte Scope {} nicht sinnlos ist. Die Datei bleibt nur solange offen, wie sie auch wirklich gebraucht wird.



  • TRWTF ist, dass MonsterName im Gegensatz zu sMonsterTP und sMonsterIniMod kein String ist...



  • thuering schrieb:

    Dummerweise ist vom "Drache" nichts mehr zu lesen, da "Das ist ein Test" die Ausgabe überschreibt.

    du hast den stream im binary mode geoeffnet. da wird dann von 'getline' nur das '\n' zeichen aus der ausgelesenen zeile rausgenommen.
    unter windows wird ein zeilenende aber durch die zeichenfolge '\r\n' repraesentiert. nun bleibt dir nach dem wort drache noch das '\r' zeichen uebrig. das zeichen bewirkt das der cursor wieder zum zeilenanfang spring. dadurch wird drache bei der naechsten textausgabe ueberschrieben.
    du musst also den stream im text-mode oeffnen, oder nach dem anwenden von getline nachsehen ob im string als letztes zeichen noch ein '\r' steht und dieses loeschen.

    Meep Meep



  • 314159265358979 schrieb:

    Beim Einlesen werden alle Leerzeichen, Newlines, Tabulatoren u.ä. übersprungen.

    Deine Lösung ist natürlich genial - allerdings werden in Zukunft wohl auch Strings mit Leerzeichen ausgelesen werden müssen (z.B. "Grüner Drache" 😉 ). Wie ließe sich das bewerkstellen, geht das irgendwie auch mit der supereleganten RAII-Methode?



  • Dann nimmst du einfach für den ersten String getline() und danach für die beiden ints den operator >>. 😉

    RAII ist übrigens ein allgemeines Konzept. Fall du es noch nicht kennst, solltest du es dir ansehen: http://en.wikipedia.org/wiki/Resource_Acquisition_Is_Initialization


  • Mod

    thuering schrieb:

    314159265358979 schrieb:

    Beim Einlesen werden alle Leerzeichen, Newlines, Tabulatoren u.ä. übersprungen.

    Deine Lösung ist natürlich genial - allerdings werden in Zukunft wohl auch Strings mit Leerzeichen ausgelesen werden müssen (z.B. "Grüner Drache" 😉 ). Wie ließe sich das bewerkstellen, geht das irgendwie auch mit der supereleganten RAII-Methode?

    Dafür haut mich volkard bestimmt:

    ifstream datei("Monster.chr");
    
        string MonsterName;
        int MonsterTP, MonsterIniMod;
        getline(datei, MonsterName) >> MonsterTP >> MonsterIniMod;
    

    Es wird geringfügig komplizierter, falls danach noch weitere getlines folgen, weil die dann denken, dass die Zeile sofort endet, da das letzte >> ein Zeilenendzeichen im Stream lässt.



  • Dann habe ich wieder das Problem des verschwindenden Strings bei der Ausgabe - s.o.
    Mit welchem String-Operator kann ich denn effektiv die letzten beiden Zeichen "\r" löschen? Oder gibt es vllt. die Möglichkeit, mit einem geeigneten Text-Editor (ich habe hier Notepad++) das \r zu löschen?



  • Vielen Dank ihr Beiden, die Sache läuft nun rund!
    Ich habe es jetzt mal mit 2 Strings hintereinander versucht, diese Lösung hier liefert alle Ergebnisse einwandfrei:

    string MonsterName, MonsterRasse;
        int MonsterTP, MonsterIniMod;
        getline(datei, MonsterName);
    	getline(datei, MonsterRasse) >> MonsterTP >> MonsterIniMod;
    

    Wenn ich es so mache, dann wird bei "Gruener Drache" das "Drache" bei der Ausgabe per cout "verschluckt":

    string MonsterName, MonsterRasse;
        int MonsterTP, MonsterIniMod;
        getline(datei, MonsterName) >> MonsterRasse >> MonsterTP >> MonsterIniMod;
    

    Ist das erklärbar oder handelt es sich hier tatsächlich um einen "Bug"?

    Achja, Inhalt der ausgelesenen Datei:

    Tharaxyxyl
    Gruener Drache
    100
    10
    

  • Mod

    Ist das erklärbar oder handelt es sich hier tatsächlich um einen "Bug"?

    Nein, das ist dann ganz normal. Oben hast du eine ganz andere Datei gepostet, der Einlesevorgang den ich dir geschreiben habe passt zu deren Format, nicht zu dem neuen. Verstehe was du tust und schreib nicht blind ab!



  • Das "Drache" wird nicht bei cout geschluckt, sondern einfach nicht mit eingelesen, weil der Input-Operator alle Whitespaces als Eingabeende betrachtet.



  • SeppJ schrieb:

    Ist das erklärbar oder handelt es sich hier tatsächlich um einen "Bug"?

    Nein, das ist dann ganz normal. Oben hast du eine ganz andere Datei gepostet, der Einlesevorgang den ich dir geschreiben habe passt zu deren Format, nicht zu dem neuen. Verstehe was du tust und schreib nicht blind ab!

    Du hast natürlich Recht, aber erstmal eine Referenz finden, die solch ein Konstrukt erklärt... In meinen Büchern steht zu deiner (sehr gut funktionierenden) Methode nichts, in meinen Videotutorials auch nicht und auch über Google habe ich nichts vergleichbares gefunden. Ich finde immer nur Beispiele vor, in denen ein Char-Array gefüllt wird oder ein einzelner String ausgelesen wird.



  • thuering schrieb:

    [...] Videotutorials [...]

    NICHT (!!) ansehen.


  • Mod

    Das Geheimnis ist, dass sämtliche Eingaben (und ausgaben übrigens auch) auf Streams den Stream selbst zurückgeben. Dadurch kann man überhaupt Konstrukte in der Form cout << a << b << c; bzw. cin >> a >> b >> c; schreiben. Das ist aber nicht auf die Operatoren >> und << beschränkt, sondern funktioniert auch hervorragend mit getline.



  • Weitere Frage zu dem Thema:
    Wie lese ich eine bestimmte Zeile (anhand Zeilennummer) aus?
    Soll ich mir da eine for-Schleife basteln, die alle Überflüssigen Lines in einen Dummy-String schreibt, um dann an der richtigen Adresse zu lesen?
    Oder gibts da andere tolle Lösungen?

    Swordfish schrieb:

    thuering schrieb:

    [...] Videotutorials [...]

    NICHT (!!) ansehen.

    OK. Auch nicht die teuer gekauften von Video2Brain oder Galileo? 🙄



  • Gezielt eine bestimmte Zeile anzusprechen geht nur, wenn du vorher schonmal die komplette Datei eingelesen hast (immer abwechselnd per getline() einlesen und per seekg() die Position mitschreiben - später kannst du dann per tellg() dorthin zurückspringen).


Anmelden zum Antworten